Migration Guide
Table of Contents
Migration Guide
Jankx 1.x to 2.0 Migration Guide
Hướng dẫn migration từ Jankx 1.x lên Jankx 2.0 framework.
🚀 Quick Migration Checklist
1. Pre-Migration Analysis
- Audit existing code for deprecated patterns
- Identify WordPress function usage
- Review service dependencies
- Check configuration access patterns
2. Core Framework Changes
- Update to new bootstrapping system
- Migrate to Config Facade
- Remove WordPressAdapter usage
- Update context detection
3. Service Layer Updates
- Convert to Service Provider pattern
- Implement proper DI
- Update error handling
- Add structured logging
4. Testing & Validation
- Update test suites
- Verify WordPress integration
- Test configuration loading
- Validate debug system
🔄 Major Architectural Changes
1. Configuration System
Old Pattern (1.x)
// ❌ OLD - Direct config access
$config = $this->config['app']['providers'];
$debugMode = $this->config['debug'] ?? false;
// ❌ OLD - ConfigManager usage
$configManager = new ConfigManager();
$value = $configManager->get('app.providers');
New Pattern (2.0)
// ✅ NEW - Config Facade
$providers = \Jankx\Facades\Config::get('app.providers.frontend', []);
$debugMode = \Jankx\Facades\Config::get('app.debug', false);
// ✅ NEW - Repository pattern
$allConfig = \Jankx\Facades\Config::all();
\Jankx\Facades\Config::set('custom.key', 'value');
2. WordPress Integration
Old Pattern (1.x)
// ❌ OLD - WordPressAdapter dependency injection
class GutenbergBlocksService
{
private $wordPressAdapter;
public function __construct(WordPressAdapter $adapter)
{
$this->wordPressAdapter = $adapter;
}
public function getBlocksInfo(): array
{
$content = $this->wordPressAdapter->getCurrentContent();
return $this->wordPressAdapter->hasBlocks($content) ? [] : [];
}
}
New Pattern (2.0)
// ✅ NEW - Direct WordPress function calls
class GutenbergBlocksService
{
public function getBlocksInfo(): array
{
$content = \get_the_content() ?: '';
return \has_blocks($content) ? $this->parseBlocks($content) : [];
}
private function isBlockEditor(): bool
{
return \is_admin() && \get_current_screen() && \get_current_screen()->is_block_editor;
}
}
3. Context Detection
Old Pattern (1.x)
// ❌ OLD - Multiple context detection methods
class DeferredServiceResolver
{
private function getCurrentContext(): string
{
if (defined('WP_CLI') && WP_CLI) return 'cli';
if (wp_doing_ajax()) return 'ajax';
if (is_admin()) return 'admin';
return 'frontend';
}
}
class UserService
{
private function getCurrentContext(): string
{
// Duplicate logic
if (defined('WP_CLI') && WP_CLI) return 'cli';
if (wp_doing_ajax()) return 'ajax';
if (is_admin()) return 'admin';
return 'frontend';
}
}
New Pattern (2.0)
// ✅ NEW - Centralized via Kernel Facade
class DeferredServiceResolver
{
public function resolve($serviceName)
{
$context = \Jankx\Facades\Kernel::getCurrentContext();
// ... rest of logic
}
}
class UserService
{
public function getUserData()
{
$context = \Jankx\Facades\Kernel::getCurrentContext();
// ... context-specific logic
}
}
4. Service Registration
Old Pattern (1.x)
// ❌ OLD - Direct service registration in Kernel
class FrontendKernel extends Kernel
{
protected function registerServices(): void
{
$this->addService('user.service', [
'class' => UserService::class,
'params' => []
]);
$this->addService('debug.info', [
'class' => DebugInfo::class,
'params' => []
]);
}
}
New Pattern (2.0)
// ✅ NEW - Service Provider pattern
class FrontendServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->singleton('user.service', UserService::class);
$this->singleton(DebugInfo::class);
}
public function boot(): void
{
// Boot logic if needed
}
}
// Kernel only registers Service Providers
class FrontendKernel extends Kernel
{
protected function registerServices(): void
{
parent::registerServices(); // Loads from Config
}
}
5. Error Handling
Old Pattern (1.x)
// ❌ OLD - Basic error handling
public function loadService(string $serviceClass): void
{
$service = new $serviceClass($this->container);
$service->register();
}
New Pattern (2.0)
// ✅ NEW - Comprehensive error handling
public function loadService(string $serviceClass): void
{
try {
if (!class_exists($serviceClass)) {
throw new \InvalidArgumentException("Service {$serviceClass} does not exist");
}
$reflection = new \ReflectionClass($serviceClass);
if ($reflection->isAbstract()) {
Logger::warning("Service Provider {$serviceClass} is abstract and cannot be instantiated");
return;
}
$service = new $serviceClass($this->container);
$service->register();
$service->boot();
Logger::debug('providerLoaded', [
'provider' => $serviceClass,
'status' => 'success'
]);
} catch (\Exception $e) {
Logger::error("Failed to load service {$serviceClass}: " . $e->getMessage());
}
}
📋 Step-by-Step Migration
Step 1: Update Configuration Access
Before (1.x)
// functions.php
$config = [
'app' => [
'providers' => [
'frontend' => [
'FrontendServiceProvider',
'DebugServiceProvider'
]
]
]
];
After (2.0)
// config/app.php
return [
'providers' => [
'frontend' => [
'Jankx\Providers\FrontendServiceProvider',
'Jankx\Providers\DebugServiceProvider',
],
],
];
Step 2: Remove WordPressAdapter Dependencies
Before (1.x)
// Service Provider
class FrontendServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->singleton(WordPressAdapter::class);
$this->singleton(GutenbergBlocksService::class);
}
}
// Service
class GutenbergBlocksService
{
private $wordPressAdapter;
public function __construct(WordPressAdapter $adapter)
{
$this->wordPressAdapter = $adapter;
}
}
After (2.0)
// Service Provider
class FrontendServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->singleton(GutenbergBlocksService::class);
}
}
// Service
class GutenbergBlocksService
{
public function getBlocksInfo(): array
{
$content = \get_the_content() ?: '';
return \has_blocks($content) ? $this->parseBlocks($content) : [];
}
}
Step 3: Update Context Detection
Before (1.x)
// Multiple classes with duplicate logic
class ServiceA
{
private function getCurrentContext(): string
{
if (defined('WP_CLI') && WP_CLI) return 'cli';
if (wp_doing_ajax()) return 'ajax';
if (is_admin()) return 'admin';
return 'frontend';
}
}
class ServiceB
{
private function getCurrentContext(): string
{
// Same logic duplicated
if (defined('WP_CLI') && WP_CLI) return 'cli';
if (wp_doing_ajax()) return 'ajax';
if (is_admin()) return 'admin';
return 'frontend';
}
}
After (2.0)
// Centralized context detection
class ServiceA
{
public function doSomething()
{
$context = \Jankx\Facades\Kernel::getCurrentContext();
// Use context
}
}
class ServiceB
{
public function doSomething()
{
$context = \Jankx\Facades\Kernel::getCurrentContext();
// Use context
}
}
Step 4: Update Error Handling
Before (1.x)
// Basic error handling
public function loadServices(): void
{
foreach ($this->serviceProviders as $providerClass) {
$provider = new $providerClass($this->container);
$provider->register();
$provider->boot();
}
}
After (2.0)
// Comprehensive error handling
public function loadServices(): void
{
foreach ($this->getServiceProviders() as $providerClass) {
Logger::debug('loadingProvider', ['provider' => $providerClass]);
if (class_exists($providerClass)) {
try {
$reflection = new \ReflectionClass($providerClass);
if ($reflection->isAbstract()) {
Logger::warning("Service Provider {$providerClass} is abstract");
continue;
}
$provider = new $providerClass($this->container);
$provider->register();
$provider->boot();
Logger::debug('providerLoaded', [
'provider' => $providerClass,
'status' => 'success'
]);
} catch (\Exception $e) {
Logger::error("Failed to load service {$providerClass}: " . $e->getMessage());
}
} else {
Logger::error("Service Provider {$providerClass} does not exist");
}
}
}
🧪 Testing Migration
1. Update Test Suites
Before (1.x)
class GutenbergBlocksServiceTest extends TestCase
{
public function testGetBlocksInfo()
{
$adapter = $this->createMock(WordPressAdapter::class);
$adapter->method('getCurrentContent')->willReturn('<!-- wp:paragraph -->');
$adapter->method('hasBlocks')->willReturn(true);
$service = new GutenbergBlocksService($adapter);
$result = $service->getBlocksInfo();
$this->assertNotEmpty($result);
}
}
After (2.0)
class GutenbergBlocksServiceTest extends TestCase
{
public function testGetBlocksInfo()
{
// Mock WordPress functions
$this->mockFunction('get_the_content', function() {
return '<!-- wp:paragraph -->';
});
$this->mockFunction('has_blocks', function($content) {
return strpos($content, '<!-- wp:') !== false;
});
$service = new GutenbergBlocksService();
$result = $service->getBlocksInfo();
$this->assertNotEmpty($result);
}
}
2. Update Integration Tests
Before (1.x)
class WordPressIntegrationTest extends TestCase
{
public function testWordPressAdapterIntegration()
{
$adapter = new WordPressAdapter();
$service = new GutenbergBlocksService($adapter);
$this->assertInstanceOf(GutenbergBlocksService::class, $service);
}
}
After (2.0)
class WordPressIntegrationTest extends TestCase
{
public function testDirectWordPressFunctionCalls()
{
$service = new GutenbergBlocksService();
// Test actual WordPress function calls
$content = \get_the_content() ?: '';
$hasBlocks = \has_blocks($content);
$this->assertIsString($content);
$this->assertIsBool($hasBlocks);
}
}
🚨 Breaking Changes
1. Removed Classes
WordPressAdapter
- Use direct WordPress function callsConfigManager
- Use Config FacadeContextualServiceRegistry
- Use Kernel Facade
2. Changed Method Signatures
Kernel::registerServices()
- Now loads from ConfigServiceProvider::register()
- Must return voidBootstrapper::bootstrap()
- Must accept Container parameter
3. New Requirements
- All services must be registered via Service Providers
- Configuration must be accessed via Config Facade
- Context detection must use Kernel Facade
- Error handling must include structured logging
🔧 Migration Tools
1. Automated Migration Script
# Run migration analysis
php bin/migrate-analyze.php
# Apply automated fixes
php bin/migrate-apply.php
2. Manual Migration Checklist
- Replace
WordPressAdapter
usage with direct function calls - Update configuration access to use Config Facade
- Replace context detection with Kernel Facade
- Convert direct service registration to Service Providers
- Add comprehensive error handling
- Update test suites
- Verify WordPress integration
📊 Migration Metrics
Success Criteria
- All WordPressAdapter dependencies removed
- Config Facade used for all configuration access
- Kernel Facade used for context detection
- Service Providers implemented for all services
- Error handling includes structured logging
- Test coverage > 80%
- No deprecated patterns remain
Performance Impact
- Memory Usage: Reduced (no adapter overhead)
- Execution Time: Improved (direct function calls)
- Code Complexity: Reduced (fewer abstractions)
🆘 Troubleshooting
Common Migration Issues
1. “Class WordPressAdapter not found”
// ❌ Problem
class MyService
{
public function __construct(WordPressAdapter $adapter)
{
$this->adapter = $adapter;
}
}
// ✅ Solution
class MyService
{
public function getCurrentContent(): string
{
return \get_the_content() ?: '';
}
}
2. “Config not accessible”
// ❌ Problem
$config = $this->config['app']['providers'];
// ✅ Solution
$providers = \Jankx\Facades\Config::get('app.providers', []);
3. “Context detection not working”
// ❌ Problem
private function getCurrentContext(): string
{
// Manual detection
}
// ✅ Solution
$context = \Jankx\Facades\Kernel::getCurrentContext();
📚 Additional Resources
Documentation
Examples
Jankx 2.0 Migration Guide - Modern WordPress Theme Framework Migration 🚀
Last updated: Development Phase Framework version: 2.0.0-dev