Learn PHP with Claude in 2026: build real skills, not AI dependency A developer warns that AI-generated PHP code is creating a generation of Laravel operators who lack fundamental PHP knowledge, citing a case where a developer using Claude copied a controller that used `$request->all()` instead of `$request->validated()`, leaving a mass assignment vulnerability. The developer argues that PHP 8.3 is a modern, typed language powering 75% of the web, and that learners must understand core PHP concepts like `$_GET`, `$_POST`, and `password_hash()` before relying on frameworks or AI. Code review last week. A "fullstack" dev shows me his Laravel API. Clean on the surface — well-organized controllers, Eloquent migrations, Form Request validation. I ask him why he's using $request- all instead of $request- validated in his controller. Blank stare. He didn't know that all returns everything in the payload, including unvalidated fields. Claude generated the controller, he copied it, it worked. Six months of Laravel and a dormant mass assignment vulnerability in every endpoint. PHP has a problem Python doesn't: its reputation. "Dead language," "spaghetti code," "only good for WordPress." In 2026, that's been wrong for a long time — PHP 8.3 is a typed, modern language with enums, fibers, readonly properties, and performance PHP 5 could never have imagined. But this reputation means many beginners skip the basics and jump straight to a framework, guided by AI. The result: Laravel operators, not PHP developers. This article explains how to use Claude to become the latter without falling into the former. Before talking pedagogy, let's settle this. "Why learn PHP when everyone's moving to Go / Rust / TypeScript?" Three facts, not opinions. 1. PHP powers 75% of the web. WordPress, Shopify backend , Symfony, Laravel — millions of projects in production. PHP gigs aren't scarce, and they pay well because good PHP devs are rare. Everyone wants to code in Rust; few can debug a Doctrine\ORM\Query\QueryException at 2am. 2. PHP 8.3 has nothing in common with PHP 5. Type declarations, enums, readonly , match , named arguments, fibers, JIT — it's a different language. PHP criticism dates back to 2012. In 2026, the same patterns people applauded in TypeScript exist natively in PHP. 3. AI doesn't debug your business context. Claude generates a UserController in 15 seconds. But when the client's LDAP auth breaks in staging and sessions no longer propagate between PHP-FPM workers, you're the one who needs to understand the HTTP request lifecycle. If you never understood $ SESSION , you're stuck. PHP has its own learning anti-patterns. AI amplifies them if you don't know they exist. You ask "how to create a contact form in PHP," Claude gives you WordPress code with wp mail and add action hooks. You copy, it works in WordPress. You learned nothing about PHP. You don't know that native mail exists, that it's terrible in production no SMTP auth, no TLS , and that PHPMailer solves the real problem. WordPress is not PHP — it's a framework with its own dialect. Laravel in week 1. It's the most common trap. The framework abstracts everything: routing, requests, responses, sessions, middleware. If you don't understand what $ GET , $ POST , $ SERVER 'REQUEST METHOD' are, you don't understand what the framework does for you. And the day it does something unexpected, you're lost. AI was trained on millions of lines of old PHP. When you ask "how to hash a password in PHP," there's a non-trivial chance Claude suggests md5 or sha1 in certain contexts. The right reflex: password hash with PASSWORD BCRYPT or PASSWORD ARGON2ID . Always check the PHP version of suggested patterns. php // ❌ PHP 5 — NEVER do this $hash = md5 $password ; $hash = sha1 $password . $salt ; // ✅ PHP 8 — the only right way $hash = password hash $password, PASSWORD ARGON2ID ; $valid = password verify $input, $hash ; PHP is optionally typed. AI often generates code without types because it's "simpler." Result: you learn a lenient PHP that accepts anything and crashes in production with TypeError s you don't understand. Enable declare strict types=1 from your very first file. No exceptions. // ❌ No typing — everything passes, nothing is safe function calculatePrice $quantity, $unitPrice { return $quantity $unitPrice; } calculatePrice "3", "19.99" ; // Works... by accident // ✅ With strict types — errors are explicit declare strict types=1 ; function calculatePrice int $quantity, float $unitPrice : float { return $quantity $unitPrice; } calculatePrice "3", "19.99" ; // Immediate TypeError Claude sometimes suggests mysqli , sometimes PDO , depending on context. The real advice: use PDO , period. It's the standard abstraction layer, it handles prepared statements cleanly, and it works with any database engine. mysqli is MySQL-specific and brings nothing extra in 99% of cases. php // ❌ mysqli — coupled to MySQL, confusing API $conn = new mysqli "localhost", "user", "pass", "db" ; $stmt = $conn- prepare "SELECT FROM users WHERE email = ?" ; $stmt- bind param "s", $email ; $stmt- execute ; $result = $stmt- get result ; // ✅ PDO — portable, clear, chainable $pdo = new PDO 'mysql:host=localhost;dbname=db', 'user', 'pass', PDO::ATTR ERRMODE = PDO::ERRMODE EXCEPTION, PDO::ATTR DEFAULT FETCH MODE = PDO::FETCH ASSOC, ; $stmt = $pdo- prepare 'SELECT FROM users WHERE email = :email' ; $stmt- execute 'email' = $email ; $user = $stmt- fetch ; The same cycle as for any language — I covered it in the Python guide https://www.web-developpeur.com/en/blog/apprendre-python-avec-ia-2026 — but with concrete PHP examples. Problem: filter an array of users to keep only adults. Your first version, without AI: // My version — filter adult users function getAdults array $users : array { $adults = ; foreach $users as $user { if $user 'age' = 18 { $adults = $user; } } return $adults; } Here's my PHP code to filter an array of users. Don't give me a corrected version. Tell me what could be improved and why, and let me rewrite it myself. Claude will point out: array filter exists for this. The return type is array but could be more precise with a @return array