Skip to content

Commit a21f2be

Browse files
koriymclaude
andcommitted
docs: Add comprehensive framework comparison
- Compare Native PHP, Laravel, Symfony, and Ray.InputQuery approaches - Highlight IDE support limitations in existing frameworks - Show magic property and string-based validation issues - Demonstrate Ray.InputQuery's type-first philosophy - Include actual framework integration code (4-line approach) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bcfc7e3 commit a21f2be

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

docs/framework-comparison.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Framework Comparison
2+
3+
A comparison of HTTP input handling approaches across popular PHP frameworks.
4+
5+
## The Problem
6+
7+
All web frameworks face the same challenge: transforming flat HTTP data into structured, type-safe PHP objects.
8+
9+
## Native PHP
10+
11+
```php
12+
// Controller
13+
public function updateProfile()
14+
{
15+
$name = $_POST['name'] ?? ''; // Manual extraction - no validation
16+
$email = $_POST['email'] ?? ''; // Manual extraction - no validation
17+
18+
// Manual file handling with error checking
19+
$avatar = null;
20+
if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
21+
$avatar = $_FILES['avatar']; // Raw array - no type safety
22+
// Manual validation: size, type, etc.
23+
if ($avatar['size'] > 2048000) {
24+
throw new Exception('File too large');
25+
}
26+
}
27+
28+
$banner = null;
29+
if (isset($_FILES['banner']) && $_FILES['banner']['error'] === UPLOAD_ERR_OK) {
30+
$banner = $_FILES['banner']; // Raw array - no type safety
31+
}
32+
33+
// Manual validation for all fields
34+
if (empty($name)) {
35+
throw new Exception('Name is required');
36+
}
37+
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
38+
throw new Exception('Invalid email');
39+
}
40+
}
41+
```
42+
43+
## Laravel
44+
45+
```php
46+
// Request class
47+
class extends FormRequest
48+
{
49+
public function rules()
50+
{
51+
return [
52+
'name' => 'required|string', // String-based rules - no IDE support
53+
'email' => 'required|email', // Typos not caught at compile time
54+
'avatar' => 'required|file|image|max:2048', // Complex string parsing
55+
'banner' => 'nullable|file|image|max:2048', // Runtime validation only
56+
];
57+
}
58+
}
59+
60+
// Controller
61+
public function updateProfile(UpdateProfileRequest $request)
62+
{
63+
$name = $request->name; // Magic property - PHPStan sees as mixed
64+
$email = $request->email; // Magic property - PHPStan sees as mixed
65+
$avatar = $request->file('avatar'); // Returns UploadedFile|null
66+
$banner = $request->file('banner'); // Returns UploadedFile|null
67+
68+
// Manual file handling and null checks required
69+
if ($avatar) {
70+
$avatarPath = $avatar->store('avatars');
71+
}
72+
}
73+
```
74+
75+
## Symfony
76+
77+
```php
78+
// Form type
79+
class ProfileType extends AbstractType
80+
{
81+
public function buildForm(FormBuilderInterface $builder, array $options)
82+
{
83+
$builder
84+
->add('name', TextType::class) // String field names - no IDE support
85+
->add('email', EmailType::class) // String field names - no IDE support
86+
->add('avatar', FileType::class, [ // String field names - no IDE support
87+
'constraints' => [new File(['maxSize' => '2M'])]
88+
])
89+
->add('banner', FileType::class, ['required' => false]); // String field names - no IDE support
90+
}
91+
}
92+
93+
// Controller
94+
public function updateProfile(Request $request)
95+
{
96+
$form = $this->createForm(ProfileType::class);
97+
$form->handleRequest($request);
98+
99+
if ($form->isValid()) {
100+
$data = $form->getData(); // Returns mixed array - no IDE support
101+
$name = $data['name']; // Array key unknown to IDE - no autocompletion
102+
$email = $data['email']; // Array key unknown to IDE - no autocompletion
103+
$avatar = $form->get('avatar')->getData(); // String field names - no IDE support
104+
}
105+
}
106+
```
107+
108+
## Ray.InputQuery
109+
110+
```php
111+
// Controller method
112+
public function updateProfile(
113+
#[Input] string $name,
114+
#[Input] string $email,
115+
#[Input] FileUpload|ErrorFileUpload $avatar,
116+
#[Input] FileUpload|ErrorFileUpload|null $banner = null,
117+
): void {
118+
// File objects are ready to use
119+
if ($avatar instanceof FileUpload) {
120+
$avatar->move('/path/to/avatars/' . $avatar->name);
121+
}
122+
}
123+
124+
// Framework integration (4 lines)
125+
$inputQuery = new InputQuery(new Injector());
126+
$method = new ReflectionMethod($controller, 'updateProfile');
127+
$args = $inputQuery->getArguments($method, $_POST);
128+
$result = $controller->updateProfile(...$args);
129+
```
130+
131+
## Key Differences
132+
133+
| Aspect | Native PHP | Laravel | Symfony | Ray.InputQuery |
134+
|--------|------------|---------|---------|----------------|
135+
| **Declaration** | Manual $_POST/$_FILES | Separate class | Separate class | Method signature |
136+
| **Type Safety** | None | Runtime validation | Runtime validation | Compile-time types |
137+
| **File Handling** | Raw arrays | Manual methods | Manual extraction | Automatic objects |
138+
| **Boilerplate** | Very High | High | High | Minimal |
139+
| **IDE Support** | None | Limited | Limited | Full type hints |
140+
141+
## Design Philosophy
142+
143+
**Native PHP**: Manual extraction and validation of HTTP data.
144+
145+
**Laravel/Symfony**: Validation-first approach with separate request classes.
146+
147+
**Ray.InputQuery**: Type-first approach using PHP's native type system and dependency injection.
148+
149+
The code speaks for itself.

0 commit comments

Comments
 (0)