This Is More Common Than You Think
Almost every freelance developer eventually inherits a legacy project. No documentation. No git history. PHP 5.6 or 7.0. MySQL queries directly in template files. A folder called final_FINAL_v3_use_this.
The client doesn't want a rewrite — they just want it to not break. Here's how to approach it professionally.
Step 1: Don't Touch Anything Yet
Before you change a single file:
- Take a full backup — files and database. Download everything via FTP. Export the database with
mysqldump. - Set up a staging copy — clone the site to a subdomain or local environment. Do all work there first.
- Enable error logging — not display, just logging. You want to see what's already broken.
// Add to the top of the main index.php or config
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/error.log');
error_reporting(E_ALL);
Step 2: Map the Site
Spend 30–60 minutes just understanding the structure before writing code.
# Count PHP files
find . -name "*.php" | wc -l
# Find the entry points
find . -name "index.php" | head -20
# Find config files (often contain DB credentials and settings)
find . -name "config.php" -o -name "settings.php" -o -name "configuration.php"
# Find files modified recently (signs of recent work or active editing)
find . -name "*.php" -mtime -30 | sort
Look for:
- Where the database connection is established
- Whether there's a framework (CodeIgniter, custom MVC, or no framework at all)
- What the URL routing looks like (mod_rewrite rules in .htaccess?)
- Whether there's a templating system or raw PHP in HTML
Step 3: Understand the Database
-- List all tables with row counts
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY table_rows DESC;
-- Find the core tables (users, orders, products, etc.)
SHOW TABLES;
Look at the biggest tables — that's where the important data lives. Check a few rows to understand the data model.
Step 4: Identify What's Actually Breaking
Check the error log you just enabled. Most legacy sites have a backlog of warnings — not all of them matter. Prioritise:
- Fatal errors — things that crash pages
- Deprecated function calls — these become fatal on PHP 8
- Database errors — failed queries, connection issues
# Count error types in the log
grep "Fatal error" error.log | wc -l
grep "Deprecated" error.log | sort | uniq -c | sort -rn | head -20
grep "Warning" error.log | sort | uniq -c | sort -rn | head -20
Step 5: Create a Safety Net Before Changing Anything
Legacy code has no tests. That means any change can break something you didn't expect.
Minimal safety net:
- List the most important pages/flows (homepage, login, checkout, key forms)
- Document what "working" looks like for each (manually, with screenshots if needed)
- After every change, manually verify each key flow still works
For anything more complex, a simple smoke test script helps:
// smoke_test.php — run after each change
$pages = [
"/",
"/login",
"/products",
"/cart",
"/checkout",
];
foreach ($pages as $page) {
$url = "http://localhost" . $page;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo ($status === 200 ? "✓" : "✗") . " [{$status}] {$page}\n";
}
Step 6: Fix in Order of Impact
Priority order for a legacy PHP site:
- Security — SQL injection, exposed credentials, file upload vulnerabilities. Non-negotiable.
- Fatal errors — things that crash. Fix these first.
- PHP compatibility — if a version upgrade is required, run phpcs to list all issues
- Deprecated warnings — clean these up systematically, they become fatals on PHP 8
- Performance — only after the above are stable
Common Security Issues in Legacy PHP
// VULNERABLE — SQL injection
$id = $_GET['id'];
$q = mysql_query("SELECT * FROM users WHERE id = $id");
// FIXED — parameterized query
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
// VULNERABLE — XSS
echo "Hello " . $_GET['name'];
// FIXED
echo "Hello " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
// VULNERABLE — file path traversal
include $_GET['page'] . '.php';
// FIXED — whitelist allowed pages
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include $page . '.php';
Deciding Whether to Rewrite
The question always comes up. My rule of thumb:
Fix and maintain if:
- The site is working and just needs to survive a PHP upgrade
- Business logic is complex and well-tested by years of production use
- Budget is limited
Rewrite if:
- Security vulnerabilities are fundamental to the architecture (can't be patched)
- The site needs new features that are impossible to add cleanly
- The codebase is so tangled that every fix breaks two other things
Most legacy sites I see are worth maintaining, not rewriting. The rewrite estimate is always 3× what the client expects, and it takes 6 months instead of 6 weeks.
If you tried the steps above and it's still not fixed (or you'd rather not do it yourself), that's when we can help.