AJAX Patterns for PHP Backends
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list eslint and semgrep as tools, with code_pattern examples like 'fetch() POST without CSRF header' and 'no response.ok check'. These are not default linter rules but require configured rules or semgrep patterns to catch reliably, placing this between default linter (d3) and specialist tool (d5); the tools listed are specialist/configured, so d5 is the best fit.
Closest to 'simple parameterised fix' (e3). The quick_fix is a small, consistent change: add CSRF token header, check response.ok, and set Accept header. This is more than a one-line patch (multiple call sites may need updating) but is a straightforward pattern replacement rather than a multi-file refactor, matching e3.
Closest to 'localised tax' (b3). The applies_to scope is web only, and the concern is limited to JavaScript/PHP integration points (AJAX calls). While every AJAX call in the codebase must follow the pattern, the burden doesn't bleed into unrelated components — it's a persistent but localised convention tax rather than a cross-cutting architectural shape.
Closest to 'serious trap' (t7). The canonical misconception is that 'PHP can automatically detect AJAX requests' — it cannot; developers must explicitly send and check headers server-side. This directly contradicts how developers familiar with frameworks (e.g. Laravel's Request::ajax() wrapper, which itself requires the header) or other ecosystems might expect automatic detection to work. Additionally, PHP silently returning HTML error pages when JSON is expected is a non-obvious failure mode that contradicts developer expectations, warranting t7.
Also Known As
TL;DR
Explanation
PHP-specific AJAX concerns: include CSRF token on every state-changing request (POST/PUT/DELETE), handle PHP's error response formats (HTML error pages vs JSON), and set Accept: application/json so PHP can detect AJAX and return JSON not a redirect. PHP side: check if request is AJAX via $_SERVER['HTTP_X_REQUESTED_WITH'] or Accept header. Classic XHR is obsolete — use fetch() with async/await everywhere.
Common Misconception
Why It Matters
Common Mistakes
- Not including CSRF token on POST requests
- Not checking response.ok before parsing JSON
- PHP returning HTML error page when JSON was expected
- Using XMLHttpRequest instead of fetch
Code Examples
// No CSRF, no error check, no content negotiation:
fetch('/api/save', {
method: 'POST',
body: JSON.stringify(data)
}).then(r => r.json()).then(console.log);
async function postToPhp(url, data) {
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name=csrf-token]').content,
},
body: JSON.stringify(data),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Server error' }));
throw new Error(err.message);
}
return res.json();
}