Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions blog/a-dotnet-developers-php-api/markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: Building a Modern PHP API as a .NET Developer
description: A practical migration mindset for .NET engineers designing clean, testable PHP APIs.
author: alex
published: 2025-09-23
tags: php, api, architecture, migration
featured: true
---

If you are used to ASP.NET (controllers, middleware pipelines, dependency injection, structured layers), modern PHP can feel both familiar and unexpectedly minimal. Below is a concise migration guide: how to think, what to adopt first, and how to avoid over‑engineering early.

## 1. Core Standards (PSRs) – Your Runtime Contract

In .NET you trust abstractions (HttpContext, IActionResult). PHP’s FIG group codified similar interoperability via PSRs. Start with:

- PSR‑4 autoloading (class → file resolution) – replaces manual include/require.
- PSR‑7 request/response objects – like HttpRequest/HttpResponse abstractions.
- PSR‑15 middleware – analogous to ASP.NET middleware pipeline.
- PSR‑17 factories – standardized object creation for PSR‑7 types.
- PSR‑11 containers – DI lookup (like IServiceProvider).
Adopt incrementally: begin with PSR‑4 (already via Composer), then PSR‑7/15 when you introduce a router + middleware.

## 2. Layered Architecture – Keep HTTP Out of Your Core

Map familiar layers:

- Domain: Pure business rules (no framework types).
- Application (Use Cases): Coordinates domain + repositories; returns simple result DTOs.
- Infrastructure: File system, database, external APIs, serialization details.
- HTTP Interface: Controllers + request mapping + response formatting.
This separation keeps your logic portable (CLI jobs, workers) and mirrors clean architecture you’d apply in .NET.

## 3. Minimal Directory Layout (Start Small)

A pragmatic starting tree:

```txt
src/
Domain/
Application/
Infrastructure/
Http/
Controller/
Middleware/
public/
index.php
api/index.php
```

Public files are your “wwwroot”; everything else is non-public. One front controller for pages (legacy can coexist) and one for API.

## 4. Request Lifecycle – Manual but Transparent

Front controller boots autoload + builds a PSR‑7 Request (via nyholm/psr7). Middleware pipeline decorates it (logging, auth, error handling). A router (nikic/fast-route) picks a handler. The controller maps input → DTO → use case, returns a domain result. A responder/serializer converts it into a JSON Response. Centralized error middleware converts exceptions into structured JSON. This explicit wiring is simpler than ASP.NET’s automatic conventions but very controllable.

## 5. Routing Choices – Pick the Lightest That Works

If you do not need a full framework, pair:

- nikic/fast-route for matching
- Custom dispatcher + minimal middleware loop
Upgrade path later: Slim, Mezzio, or Symfony HTTP Kernel if complexity justifies (events, DI, caching).

## 6. DTOs & Serialization – Avoid Leaking Internals

Return lightweight arrays or dedicated Response DTOs (hydrated into arrays) rather than dumping domain entities. You can add Symfony Serializer later if you hit nested graph complexity. Early clarity > heavy tooling.

## 7. Error Handling – One Translation Point

Central middleware catches:

- Validation or input errors → 400
- Not found → 404
- Domain rule violations (e.g., invariant failures) → 422
- Conflict (versioning / uniqueness) → 409
Wrap them in a consistent JSON envelope or RFC 7807 (application/problem+json). This avoids scattering try/catch blocks across controllers.

## 8. Validation – Keep It Close to the Edge

Parse & validate raw input in controller or a dedicated InputValidator before calling the use case. For richer rules later: symfony/validator. Do not embed raw $_GET / $_POST usage deep in services; normalize once.

## 9. Authentication & Authorization – Middleware First

Stateless Bearer tokens (JWT) keep the API horizontally scalable. Middleware resolves identity → attaches UserIdentity to Request attributes. Downstream authorization occurs either in a policy service or inside application layer methods (explicit guard calls). Mirrors ASP.NET’s ClaimsPrincipal pipeline.

## 10. Versioning – Plan Early, Implement Lightly

Simplest: prefix path `/api/v1/...`. Freeze contract per version; evolutionary changes stay backward compatible. Add a response header `X-API-Version: 1`. Defer media-type versioning unless real need emerges.

## 11. Caching – Leverage HTTP Properly

For read-heavy endpoints:

- ETag or Last-Modified on GET responses (based on content hash or updated_at).
- Respect If-None-Match / If-Modified-Since to return 304 quickly.
Server-side: PSR‑16 simple cache (filesystem or Redis) around pure read use cases. Avoid premature caching of personalized responses (Vary headers if needed).

## 12. Pagination & Query Consistency

Adopt predictable params:

- page (1-based), per_page (cap it), sort (field or -field for desc), filter[name]=value pattern.
Return meta:

```json
{
"data": [...],
"meta": { "page": 1, "per_page": 20, "total": 54, "total_pages": 3 }
}
```

This predictability reduces ad hoc branching later.

## 13. Security Foundations

Enforce HTTPS (terminate early if not). Strip or validate unknown query params to limit accidental surface. Sanitize output via JSON encoding (automatic). Rate limiting (simple token bucket in Redis) before business logic prevents accidental abuse. Log security-relevant denials uniformly.

## 14. Documentation – Contract as a Living Asset

Draft a slim OpenAPI spec as soon as first endpoint stabilizes. Even a partial document clarifies naming (snake_case vs camelCase), envelope structure, and error formats. Use it later for:

- Schema validation tests
- Client generation (TypeScript, C#)
Keep it versioned with the codebase—no hidden wiki drift.

## 15. Testing Strategy – Lean but Layered

- Domain & Application: pure unit tests (fast; no HTTP).
- HTTP Integration: spin kernel in-memory; assert status + JSON schema.
- Contract Tests: validate responses vs OpenAPI to avoid silent breaking changes.
Add mutation testing or static analysis (PHPStan) once green path stable.

## 16. Incremental Adoption Plan (Your First Week)

Day 1–2: Add dependencies (PSR‑7, fast-route). Create api/index.php with a single /health endpoint.
Day 3: Introduce Blog/Portfolio feature endpoints reusing existing services (wrap them with DTO mappers).
Day 4: Add error middleware + uniform JSON envelope + pagination pattern.
Day 5: Introduce OpenAPI draft + simple auth placeholder middleware.
Day 6: Add caching (ETag) to featured lists.
Day 7: Harden tests + refine directory structure; document contribution guidelines for endpoints.

## 17. Migration Mindset from .NET

Things you might miss:

- Convention-over-configuration scaffolding. (You wire more manually.)
- DI container magic. (Add PHP-DI or Symfony DI only when constructor lists grow.)
- Built-in model binding. (Write explicit mapping; it stays transparent.)
Upside: Extremely low abstraction overhead early on; performance and clarity are good; incremental complexity feels linear.

## 18. When to Add More Framework

Introduce heavier tools only at clear pain thresholds:

- Repetitive manual wiring → DI container.
- Sprawling serialization logic → Serializer component.
- Cross-cutting metrics/tracing growth → Observability middleware + OpenTelemetry SDK.

## 19. Deployment & Runtime Notes

API is just PHP scripts invoked by the web server (FPM). Ensure:

- Composer autoload optimized (`composer install --no-dev -o`).
- Opcache enabled in production.
- Separate `public/` as docroot (avoid leaking source).
Rolling updates = upload new code, let Opcache warm, optionally include a version endpoint.

## 20. Minimal Example Endpoint Shape (Conceptual)

Controller returns:

```json
{
"data": { "id": "post-123", "title": "..." },
"meta": { "request_id": "abc123", "generated_at": "2025-09-23T12:30:00Z" }
}
```

Errors:

```json
{
"error": { "type": "validation_error", "message": "Invalid page size", "fields": { "per_page": "Must be <= 100" } }
}
```

Predictable envelopes reduce per-endpoint cognitive load.

---

Adopt only what solves today’s friction; keep the door open for structured evolution. Start tiny, standardize early naming, and let real usage drive each escalation (container, serializer, auth, versioning). That discipline prevents the “accidental monolith” feeling while still staying fast.

Happy building.
2 changes: 1 addition & 1 deletion blog/blog-system/markdown.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "Dynamic Blog Engine"
title: "Building your own Blog Engine"
subtitle: "Building a Flexible Blog Engine with PHP, Twig, and Markdown"
post: "blog-system"
description: "Designing and implementing a lightweight, markdown-powered blog system that transforms static files into dynamic content while maintaining clean architecture and optimal performance."
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ services:
dockerfile: docker/php/Dockerfile
container_name: dopetrope-php
volumes:
- ./:/var/www/html:delegated
- ./:/var/www/html
environment:
PHP_DISPLAY_ERRORS: 1
DISABLE_TWIG_CACHE: 1
networks:
- dopetrope-net

Expand Down
43 changes: 6 additions & 37 deletions includes/i18n/translations/de.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
{
"site": {
"name": "TWIːK",
"description": "Eine responsive HTML5 + CSS3 Website-Vorlage, entworfen von HTML5 UP und kostenlos unter der Creative Commons Lizenz veröffentlicht.",
"description": "Eine moderne, responsive Website-Vorlage – frei nutzbar unter Creative‑Commons Lizenz und geschaffen, um Ideen sichtbar zu machen.",
"author": "tweakch"
},
"nav": {
"home": "Startseite",
"blog": "Blog",
"left_sidebar": "Linke Seitenleiste",
"right_sidebar": "Rechte Seitenleiste",
"no_sidebar": "Keine Seitenleiste"
"portfolio": "Portfolio "
},
"banner": {
"title": "Alexander Klee",
Expand All @@ -27,11 +25,11 @@
"intro": {
"section1": {
"title": "Über mich",
"description": "Ich bin ein leidenschaftlicher Entwickler, spezialisiert auf PHP und moderne Webtechnologien. Willkommen in meinem Portfolio!"
"description": "Ich bin ein leidenschaftlicher Entwickler, der gerne Ideen in erlebbare digitale Erlebnisse verwandelt. Willkommen in meinem Portfolio!"
},
"section2": {
"title": "Fähigkeiten",
"description": "Erfahren in PHP, JavaScript, HTML5, CSS3 und Frameworks wie Laravel und React. Ich erstelle skalierbare und wartbare Lösungen."
"description": "Ich gestalte strukturierte, klare und nachhaltige Lösungen – mit Fokus auf Qualität, Verständlichkeit und Weiterentwicklung."
},
"section3": {
"title": "Kontakt",
Expand Down Expand Up @@ -89,39 +87,10 @@
}
},
"footer": {
"recent_posts": {
"title": "Neueste Beiträge",
"posts": [
{
"date": "27. Jan",
"title": "Lorem dolor sit amet veroeros",
"excerpt": "Ipsum dolor sit amet veroeros consequat blandit ipsum phasellus lorem consequat etiam."
},
{
"date": "23. Jan",
"title": "Ipsum sed blandit nisl consequat",
"excerpt": "Blandit phasellus lorem ipsum dolor tempor sapien tortor hendrerit adipiscing feugiat lorem."
},
{
"date": "15. Jan",
"title": "Magna tempus lorem feugiat",
"excerpt": "Dolore consequat sed phasellus lorem sed etiam nullam dolor etiam sed amet sit consequat."
},
{
"date": "12. Jan",
"title": "Dolore tempus ipsum feugiat nulla",
"excerpt": "Feugiat lorem dolor sed nullam tempus lorem ipsum dolor sit amet nullam consequat."
},
{
"date": "10. Jan",
"title": "Blandit tempus aliquam?",
"excerpt": "Feugiat sed tempus blandit tempus adipiscing nisl lorem ipsum dolor sit amet dolore."
}
]
},
"recent_posts": { "title": "Neueste Beiträge" },
"about": {
"title": "Worum geht es hier?",
"description": "Dies ist <strong>Dopetrope</strong>, eine kostenlose, vollständig responsive HTML5-Website-Vorlage von AJ für HTML5 UP. Sie wird kostenlos unter der Creative Commons Attribution-Lizenz veröffentlicht, also verwenden Sie sie gerne für jedes persönliche oder kommerzielle Projektvergessen Sie nur nicht, uns zu erwähnen!",
"description": "Dies ist <strong>Dopetrope</strong>, eine frei verfügbare, vollständig responsive Vorlage. Nutze sie gerne für persönliche oder kommerzielle Projekteein kurzer Hinweis auf die ursprüngliche Quelle genügt und freut uns!",
"find_out_more": "Mehr erfahren"
},
"links": {
Expand Down
45 changes: 7 additions & 38 deletions includes/i18n/translations/en.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
{
"site": {
"name": "TWIːK",
"description": "A responsive HTML5 + CSS3 site template designed by HTML5 UP and released for free under the Creative Commons license.",
"description": "A modern, responsive site concept – freely usable under a Creative Commons license and built to let ideas travel fast.",
"author": "tweakch"
},
"nav": {
"home": "Home",
"blog": "Blog",
"left_sidebar": "Left Sidebar",
"right_sidebar": "Right Sidebar",
"no_sidebar": "No Sidebar"
"portfolio": "Portfolio"
},
"banner": {
"title": "Alexander Klee",
Expand All @@ -27,15 +25,15 @@
"intro": {
"section1": {
"title": "About Me",
"description": "I'm a passionate developer specializing in PHP and modern web technologies. Welcome to my portfolio!"
"description": "I love turning raw ideas into tangible digital experiences. Welcome to my portfolio!"
},
"section2": {
"title": "Skills",
"description": "Experienced in PHP, JavaScript, HTML5, CSS3, and frameworks like Laravel and React. I build scalable and maintainable solutions."
"description": "I'm passionate about sharing knowledge with others to inspire and grow together."
},
"section3": {
"title": "Contact",
"description": "Interested in collaboration or have a question? Contact me via email or through the contact form below."
"description": "Interested in a collaboration or have a question? Don't hesitate to contact me."
},
"get_started": "Get Started",
"learn_more": "Learn More",
Expand Down Expand Up @@ -89,39 +87,10 @@
}
},
"footer": {
"recent_posts": {
"title": "Recent Posts",
"posts": [
{
"date": "Jan 27",
"title": "Lorem dolor sit amet veroeros",
"excerpt": "Ipsum dolor sit amet veroeros consequat blandit ipsum phasellus lorem consequat etiam."
},
{
"date": "Jan 23",
"title": "Ipsum sed blandit nisl consequat",
"excerpt": "Blandit phasellus lorem ipsum dolor tempor sapien tortor hendrerit adipiscing feugiat lorem."
},
{
"date": "Jan 15",
"title": "Magna tempus lorem feugiat",
"excerpt": "Dolore consequat sed phasellus lorem sed etiam nullam dolor etiam sed amet sit consequat."
},
{
"date": "Jan 12",
"title": "Dolore tempus ipsum feugiat nulla",
"excerpt": "Feugiat lorem dolor sed nullam tempus lorem ipsum dolor sit amet nullam consequat."
},
{
"date": "Jan 10",
"title": "Blandit tempus aliquam?",
"excerpt": "Feugiat sed tempus blandit tempus adipiscing nisl lorem ipsum dolor sit amet dolore."
}
]
},
"recent_posts": { "title": "Recent Posts" },
"about": {
"title": "What's this all about?",
"description": "This is <strong>Dopetrope</strong> a free, fully responsive HTML5 site template by AJ for HTML5 UP. It's released for free under the Creative Commons Attribution license so feel free to use it for any personal or commercial projectjust don't forget to credit us!",
"description": "This is <strong>Dopetrope</strong>, a free, fully responsive template. Use it for personal or commercial projectsa brief credit is appreciated and helps it live on.",
"find_out_more": "Find out more"
},
"links": {
Expand Down
Loading