loadSettings(); } /** * Settings'den ayarları yükler */ private function loadSettings(): void { // Settings class'ı mevcutsa kullan if (class_exists('\\AllDataMigration\\Engine\\Settings')) { $settings = Settings::getInstance(); $this->debugMode = (bool) $settings->get('advanced_debug_mode', false); $this->paranoidMode = (bool) $settings->get('advanced_paranoid_mode', true); $this->jsonEscapeSlashes = (bool) $settings->get('advanced_json_escape_slashes', false); } } /** * Debug modunu manuel ayarla */ public function setDebugMode(bool $enabled): self { $this->debugMode = $enabled; return $this; } /** * Paranoid modu manuel ayarla */ public function setParanoidMode(bool $enabled): self { $this->paranoidMode = $enabled; return $this; } /** * Değişim çifti ekler */ public function addReplacement(string $search, string $replace): self { if ($search !== $replace) { $this->replacements[$search] = $replace; } return $this; } /** * Tüm değişimleri temizler */ public function clearReplacements(): self { $this->replacements = []; return $this; } /** * Ana işlem metodu - her türlü veriyi işler */ public function process($data) { if (is_null($data)) { return $data; } if (is_string($data)) { return $this->processString($data); } if (is_array($data)) { return $this->processArray($data); } if (is_object($data)) { return $this->processObject($data); } return $data; } /** * String işleme - serialize, JSON veya düz string */ private function processString(string $data): string { if (empty($data)) { return $data; } // --- DEBUG TRACE --- if ($this->debugMode) { $this->logTrace("Gelen Veri: " . substr($data, 0, 100)); } // Serialize edilmiş veri kontrolü if ($this->isSerialized($data)) { if ($this->debugMode) $this->logTrace(">> Tespit: SERIALIZED"); return $this->processSerializedString($data); } // JSON kontrolü if ($this->isJson($data)) { if ($this->debugMode) $this->logTrace(">> Tespit: JSON"); return $this->processJson($data); } // Base64 serialize kontrolü if ($this->isBase64Serialized($data)) { if ($this->debugMode) $this->logTrace(">> Tespit: BASE64"); return $this->processBase64Serialized($data); } // Düz string replace if ($this->debugMode) $this->logTrace(">> Tespit: DÜZ TEXT"); return $this->replaceInString($data); } /** * Debug Log Yazıcı - Sadece debug modu açıksa çalışır */ private function logTrace(string $msg): void { if (!$this->debugMode) { return; } $time = gmdate('H:i:s'); $line = "[$time] TC: $msg\n"; if (defined('ALLDAMI_STORAGE_DIR')) { @file_put_contents(ALLDAMI_STORAGE_DIR . 'cloner_trace.txt', $line, FILE_APPEND); } } /** * Serialize edilmiş string işleme */ private function processSerializedString(string $data): string { $unserialized = @unserialize($data); if ($unserialized === false && $data !== 'b:0;') { // Bozuk serialize veya class yüklenemedi // Düz replace yaparsak veri bozulur, bu yüzden olduğu gibi bırak // error_log('WP Cloner: Serialize verisi çözülemedi, atlanıyor. Veri başlangıcı: ' . substr($data, 0, 50)); return $data; } // Recursive işle $processed = $this->process($unserialized); // Tekrar serialize et return serialize($processed); } /** * JSON string işleme - MIXED CONTENT DESTEĞİ */ private function processJson(string $data): string { $decoded = json_decode($data, true); if (json_last_error() !== JSON_ERROR_NONE) { return $this->replaceInString($data); } // Mixed content işleme (JSON içindeki serialize verileri) $processed = $this->processMixedContent($decoded); // Configurable JSON encode flags $flags = JSON_UNESCAPED_UNICODE; if (!$this->jsonEscapeSlashes) { $flags |= JSON_UNESCAPED_SLASHES; } return json_encode($processed, $flags); } /** * Mixed Content İşleme - JSON içindeki serialize verileri tespit eder * Elementor, Gutenberg, WPBakery uyumluluğu */ private function processMixedContent($data) { if (is_null($data)) { return $data; } if (is_string($data)) { // Boş string if (empty($data)) { return $data; } // Serialize edilmiş veri if ($this->isSerialized($data)) { return $this->processSerializedString($data); } // Escaped serialize kontrolü: s:45:\"...\" (JSON içinde) if ($this->containsEscapedSerialize($data)) { return $this->processEscapedSerialize($data); } // Düz string replace return $this->replaceInString($data); } if (is_array($data)) { $result = []; foreach ($data as $key => $value) { $newKey = is_string($key) ? $this->replaceInString($key) : $key; $result[$newKey] = $this->processMixedContent($value); } return $result; } if (is_object($data)) { return $this->processObject($data); } return $data; } /** * Escaped serialize kontrolü * JSON içinde: s:45:\"https://...\" veya a:2:{...} */ private function containsEscapedSerialize(string $data): bool { // Pattern: s:N:\" veya a:N:{ veya O:N:\" // Escaped quotes: \" veya normal serialize syntax $patterns = [ '/s:[0-9]+:\\\\"/', // s:45:\" (escaped in JSON) '/s:[0-9]+:"[^"]*"/', // s:45:"..." (normal) '/a:[0-9]+:\{/', // a:2:{ (array) '/O:[0-9]+:"/', // O:8:"stdClass" (object) ]; foreach ($patterns as $pattern) { if (preg_match($pattern, $data)) { return true; } } return false; } /** * Escaped serialize verilerini işler * JSON'dan gelen backslash'lı serialize verileri */ private function processEscapedSerialize(string $data): string { // JSON escape'lerini çöz $unescaped = stripslashes($data); // Tam serialize mi? if ($this->isSerialized($unescaped)) { $processed = $this->processSerializedString($unescaped); // Tekrar escape et (JSON içine geri koyacak) return addslashes($processed); } // Kısmi serialize içeriyor olabilir - URL'leri değiştir // Pattern: s:N:"URL"; formatlarını bul ve değiştir $result = preg_replace_callback( '/s:([0-9]+):"([^"]*)";/', function ($matches) { $content = $matches[2]; // URL değişimi yap foreach ($this->replacements as $search => $replace) { $content = str_replace($search, $replace, $content); } // Yeni uzunluk ile serialize formatı return 's:' . strlen($content) . ':"' . $content . '";'; }, $unescaped ); // Tekrar escape et return addslashes($result); } /** * Base64 serialize işleme */ private function processBase64Serialized(string $data): string { $decoded = base64_decode($data, true); if ($decoded === false || !$this->isSerialized($decoded)) { return $this->replaceInString($data); } $processed = $this->processSerializedString($decoded); return base64_encode($processed); } /** * Array işleme */ private function processArray(array $data): array { $result = []; foreach ($data as $key => $value) { // Key'lerde de replace yap $newKey = is_string($key) ? $this->replaceInString($key) : $key; $result[$newKey] = $this->process($value); } return $result; } /** * Object işleme */ private function processObject(object $obj): object { // __PHP_Incomplete_Class kontrolü (Eksik plugin class'ları) // Bu nesnelere dokunursak yapıları bozulur ve tekrar serialize edildiğinde hatalı olurlar. if ($obj instanceof \__PHP_Incomplete_Class) { // Debug için loglayabiliriz // error_log('WP Cloner: Incomplete Class atlandı - Veri bütünlüğü korundu.'); return $obj; } // stdClass if ($obj instanceof \stdClass) { $result = new \stdClass(); foreach (get_object_vars($obj) as $key => $value) { $newKey = $this->replaceInString($key); $result->$newKey = $this->process($value); } return $result; } // Diğer objeler - property'leri işle $reflection = new \ReflectionObject($obj); foreach ($reflection->getProperties() as $property) { $property->setAccessible(true); $value = $property->getValue($obj); $property->setValue($obj, $this->process($value)); } return $obj; } /** * Düz string replace */ private function replaceInString(string $data): string { // PARANOID MODE (Configurable) // Serialize benzeri bir şey görürsek atlayalım if ($this->paranoidMode) { if (preg_match('/(?:s|a|O|b|i|d|C):[0-9]+[:;]/', $data) || trim($data) === 'N;') { return $data; } } // Güvenliyse değiştir foreach ($this->replacements as $search => $replace) { $data = str_replace($search, $replace, $data); } return $data; } /** * Serialize kontrolü */ private function isSerialized(string $data): bool { // WordPress fonksiyonunu kullan if (function_exists('is_serialized')) { return is_serialized($data); } // Fallback (WP dışı kullanım için) if ($data === 'b:0;' || $data === 'b:1;') { return true; } if (strlen($data) < 4) { return false; } if ($data[1] !== ':') { return false; } $token = $data[0]; switch ($token) { case 's': // string return substr($data, -2) === '";' || substr($data, -1) === ';'; case 'a': // array case 'O': // object return (bool) preg_match('/^' . $token . ':[0-9]+:/s', $data); case 'i': // integer case 'd': // double return (bool) preg_match('/^' . $token . ':[0-9.E+-]+;$/s', $data); case 'b': // boolean return (bool) preg_match('/^' . $token . ':[01];$/s', $data); case 'N': // null return $data === 'N;'; case 'C': // custom object return (bool) preg_match('/^' . $token . ':[0-9]+:/s', $data); } return false; } /** * JSON kontrolü */ private function isJson(string $data): bool { if (empty($data)) { return false; } $first = $data[0]; $last = $data[strlen($data) - 1]; if (($first === '{' && $last === '}') || ($first === '[' && $last === ']')) { json_decode($data); return json_last_error() === JSON_ERROR_NONE; } return false; } /** * Base64 serialize kontrolü */ private function isBase64Serialized(string $data): bool { if (!preg_match('/^[a-zA-Z0-9+\/=]+$/', $data)) { return false; } $decoded = base64_decode($data, true); if ($decoded === false) { return false; } return $this->isSerialized($decoded); } /** * Veritabanı satırını işle */ /** * Veritabanı satırını işle */ public function processTableRow(array $row, array $textColumns = []): array { // Güvenliyse işle foreach ($row as $column => $value) { if (is_string($value) && !empty($value)) { $row[$column] = $this->process($value); } } return $row; } /** * Replacement listesini döndür */ public function getReplacements(): array { return $this->replacements; } }