Every line of PHP on shinobis.com was written with Claude as my coding partner. Every function, every query, every PHP file over the last six months. I do not copy and paste without reviewing. But I do trust the output after verifying it. Most of the time it works.

Three times it did not. And each time I learned something about PHP that six months of tutorials would not have taught me. Not because tutorials do not cover those topics. Because bugs that break your code in production teach at a level that reading about the topic never reaches.

Bug 1: require_once executes code

The auto-linker is a script that runs as a daily cron at 6AM. It reads all published posts, analyzes the connections between them, and updates internal links automatically. Claude wrote it as a direct executable script: opens the database connection, queries the posts, and processes everything in the global scope of the file.

The problem appeared when I needed to use a function from the auto-linker inside admin.php. I added a require_once for the file. PHP loaded the file, and by loading it executed all the global scope logic. Every time I opened the admin panel, the auto-linker processed every published post. The page took 8 seconds to load.

The code before the fix:

// auto-linker.php
require_once __DIR__ . '/functions.php';

// This logic ran EVERY TIME admin.php did require_once
$pdo = getDB();
$posts = $pdo->query("SELECT * FROM posts WHERE status = 'published'");
// ... processed all posts every time admin.php loaded

The fix:

// auto-linker.php
require_once __DIR__ . '/functions.php';

// Only execute when called directly, not when included
if (basename($_SERVER['SCRIPT_FILENAME']) === basename(__FILE__)) {
    $pdo = getDB();
    $posts = $pdo->query("SELECT * FROM posts WHERE status = 'published'");
    // ... only runs when the cron calls it directly
}

// Functions are available for require_once without executing logic
function addAutoLinks($postId, $langCode) {
    // ...
}

What I learned: require_once in PHP does not just make functions available. It loads AND executes all code in the global scope of the file. Claude generated the script as a direct executable because that is what I asked for. It did not anticipate that another file would include it later. A developer with PHP experience knows that any file that might be included needs to protect its execution logic. Claude did not make that distinction because it had no context of the full system.

Bug 2: Two functions with the same name

The admin panel grew over weeks. In one session I asked Claude for a function to update site settings. It named it handleUpdateLinks. The name was not ideal but it worked. Two hundred lines later, in a different session, I needed a function to update post links. Claude named it handleUpdateLinks.

PHP does not allow two functions with the same name. Fatal error: Cannot redeclare function.

The code before the fix:

// admin.php

function handleUpdateLinks() {
    // This function updated SETTINGS
    $siteName = $_POST['site_name'];
    updateSetting('site_name', $siteName);
}

// ... 200 lines later ...

function handleUpdateLinks() {
    // This function updated post LINKS
    $postId = $_POST['post_id'];
    // PHP fatal error: Cannot redeclare function
}

The fix was renaming the first function to handleUpdateSettings. Trivial. What is interesting is why it happened.

Claude does not have memory between sessions. When it generated the second function, it did not know the first one existed. It named both with the logical convention for what they did at that moment. The crash was inevitable. A developer working on the same file for weeks would have noticed the conflict. Claude, which sees each session as a new universe, cannot.

What I learned: AI is excellent at generating individual functions. It is bad at maintaining consistency in a file that grows across multiple sessions. The responsibility for system coherence belongs to the human, not the tool.

Bug 3: echo before header()

The markdown-negotiator.php has two modes: serve the homepage as Markdown, or serve an individual post as Markdown. Claude wrote both modes in the same file but with different patterns.

For the homepage it used direct echo:

if ($slug === null) {
    $siteName = getLocalizedSetting('site_name', $langCode);
    
    echo "# " . $siteName . "\n\n";
    echo "> " . $siteDesc . "\n\n";
    echo "## Recent Posts\n\n";
    
    // ... more echos ...
    
    header('x-markdown-tokens: ' . $tokenEstimate);
    // CRASH: Cannot modify header information - headers already sent
}

For individual posts it used concatenation in a $output variable and sent the header before the echo. The correct pattern. But it did not apply it to the homepage.

The fix:

if ($slug === null) {
    $siteName = getLocalizedSetting('site_name', $langCode);
    
    $output = "# " . $siteName . "\n\n";
    $output .= "> " . $siteDesc . "\n\n";
    $output .= "## Recent Posts\n\n";
    
    // ... more concatenation ...
    
    $tokenEstimate = (int)(str_word_count($output) * 1.3);
    header('x-markdown-tokens: ' . $tokenEstimate);
    
    echo $output;
}

PHP requires that all header() calls happen before any output to the browser. Claude knows this rule. It applied it correctly in one section of the file. It did not apply it in another section of the same file. The inconsistency is not a knowledge error. It is an attention error. AI does not review its own code against itself. It was Gemini, during an audit of the file, that caught this bug.

What all three bugs have in common

None of them are syntax errors. All three are context errors. Claude knows that require_once executes code. It knows that PHP does not allow duplicate functions. It knows that header() goes before echo. But it does not have the full-system view that a human developer builds by working on the same project for months.

The first bug is a lack of architectural context. Claude did not know another file would include the script. The second is a lack of memory between sessions. Claude did not remember the previous function. The third is a lack of internal consistency. Claude applied the correct pattern in one place and not in another within the same file.

Each of these bugs taught me something specific about PHP that I would not have learned by reading documentation. The difference between require_once that loads and executes versus include behavior. The basename pattern for protecting cron scripts. The headers-before-output rule and why it exists at the HTTP protocol level.

I did not learn these things because I wanted to study PHP. I learned them because my code broke in production and I had to understand why. AI generated the code. The bug forced me to understand it. The result is that I am a better developer than I was before each bug.

Using AI as a coding partner does not replace learning. It redirects it. Instead of learning from tutorials, you learn from the mistakes of your tool. And a bug in production teaches faster than any tutorial.