Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример принятия легаси-кода в PHP
Принятие легаси-кода — это процесс анализа, документирования и постепенного улучшения унаследованной системы. Рассмотрим практический пример из моей практики — модуль обработки заказов в старом интернет-магазине на PHP 5.4 без использования фреймворка.
Исходная ситуация
<?
// Файл: /legacy/order_process.php
include_once('config.php');
session_start();
if (isset($_POST['order_id'])) {
$order_id = mysql_real_escape_string($_POST['order_id']);
$sql = "SELECT * FROM orders WHERE id = '$order_id'";
$result = mysql_query($sql);
$order = mysql_fetch_assoc($result);
// Логика обработки
if ($order['status'] == 'new') {
// 200 строк спагетти-кода с SQL-запросами,
// смешанной бизнес-логикой и HTML-выводом
echo "Заказ обработан";
}
}
?>
Проблемы:
- Устаревший MySQL extension (опасность SQL-инъекций)
- Смешение логики, данных и представления
- Отсутствие автозагрузки классов и ООП
- Глобальные зависимости (config.php, сессии)
- Нулевая тестируемость
Стратегия принятия легаси
Шаг 1: Анализ и создание безопасности
Первым делом я создал обертку безопасности для критических функций:
// Файл: /wrappers/DatabaseSafeWrapper.php
class DatabaseSafeWrapper {
private static $pdo;
public static function getConnection() {
if (!self::$pdo) {
self::$pdo = new PDO(
'mysql:host=' . LEGACY_DB_HOST . ';dbname=' . LEGACY_DB_NAME,
LEGACY_DB_USER,
LEGACY_DB_PASS,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
}
return self::$pdo;
}
public static function safeQuery($sql, $params = []) {
$stmt = self::getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt;
}
}
Шаг 2: Постепенная рефакторинг с сохранением функциональности
Вместо полной перезаписи, я применил стратегию шаг за шагом:
// Файл: /refactored/OrderProcessor.php
class OrderProcessor {
private $orderRepository;
private $statusCalculator;
public function __construct(OrderRepository $repository) {
$this->orderRepository = $repository;
}
public function process($orderId) {
// 1. Берем данные через новый безопасный метод
$order = $this->orderRepository->findById($orderId);
// 2. Старую логику пока оставляем, но изолируем
if ($order->getStatus() === 'new') {
$this->legacyProcessingLogic($order);
}
return $order;
}
/**
* Временный метод-адаптер для старой логики
* Постепенно будем разбирать его на части
*/
private function legacyProcessingLogic($order) {
// Временно вызываем старый код через адаптер
LegacyOrderAdapter::process($order->getId());
}
}
Шаг 3: Создание адаптеров и фасадов
Адаптер-паттерн стал ключевым инструментом:
// Файл: /adapters/LegacyOrderAdapter.php
class LegacyOrderAdapter {
public static function process($orderId) {
// Включаем старый файл, но в контролируемой среде
ob_start();
// Имитируем глобальные переменные старой системы
$_POST['order_id'] = $orderId;
$_SESSION = LegacySession::getData();
// Подключаем legacy-код через буфер
include_once(__DIR__ . '/../legacy/order_process.php');
$output = ob_get_clean();
// Парсим вывод старой системы
return self::parseLegacyOutput($output);
}
}
Шаг 4: Внедрение тестов
Создал характеризационные тесты (golden master tests):
// Файл: /tests/LegacyOrderProcessingTest.php
class LegacyOrderProcessingTest extends PHPUnit_Framework_TestCase {
public function testLegacyBehaviorPreserved() {
// Захватываем текущее поведение как эталон
$result = LegacyOrderAdapter::process(123);
// Фиксируем ожидаемый вывод
$this->assertStringContainsString('Заказ обработан', $result);
// Проверяем побочные эффекты в БД
$dbState = $this->getDatabaseState();
$this->assertEquals('processed', $dbState['orders']['status']);
}
}
Ключевые принципы принятия легаси
Основные правила, которые я использовал:
- Не ломать то, что работает — сначала пишем тесты, потом меняем
- Изоляция рисков — опасный код оборачиваем в безопасные обертки
- Постепенность — маленькие изменения с немедленной проверкой
- Документирование — создаем карту зависимостей и бизнес-правил
- Дублирование, затем устранение — создаем чистую реализацию параллельно со старой, затем переключаем
Результаты подхода
Через 3 месяца работы по этой стратегии:
- Критический код был защищен от SQL-инъекций
- 30% кодовой базы переведено на современный PHP 7.4 с типами
- Появились юнит-тесты для ключевых бизнес-процессов
- Новые разработчики смогли начать работу с системой, изучая сначала адаптеры, а не спагетти-код
- Производительность выросла на 40% за счет оптимизации запросов
Важный вывод: Принятие легаси — это не про мгновенную перезапись, а про стратегическое управление техническим долгом с минимальным риском для бизнеса. Лучше иметь работающую legacy-систему с планом улучшений, чем сломать производство ради "идеального" кода.