RI websocket performance
This commit is contained in:
parent
2849f0fe5f
commit
c76e6ae111
|
|
@ -59,12 +59,7 @@ class ControllerResolver
|
||||||
return self::$controllerCache[$eventPrefix];
|
return self::$controllerCache[$eventPrefix];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure controllers are scanned
|
// Try to find the controller (skip scanning in forked children - classes are already loaded)
|
||||||
if (!self::$scanned) {
|
|
||||||
self::scanControllers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find the controller
|
|
||||||
$controllerClass = self::findController($eventPrefix);
|
$controllerClass = self::findController($eventPrefix);
|
||||||
|
|
||||||
// Cache the result (even if null, to avoid repeated lookups)
|
// Cache the result (even if null, to avoid repeated lookups)
|
||||||
|
|
@ -75,60 +70,58 @@ class ControllerResolver
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find controller using multiple strategies
|
* Find controller using multiple strategies
|
||||||
|
* Optimized for speed: most common case (direct match) checked first
|
||||||
*/
|
*/
|
||||||
private static function findController(string $eventPrefix): ?string
|
private static function findController(string $eventPrefix): ?string
|
||||||
{
|
{
|
||||||
// Strategy 1: Direct match (e.g., 'app' → 'AppController')
|
// Strategy 1: Direct match in app namespace (most common case)
|
||||||
|
// e.g., 'app' → '\App\Websocket\Controllers\AppController'
|
||||||
$directName = self::kebabToPascal($eventPrefix) . 'Controller';
|
$directName = self::kebabToPascal($eventPrefix) . 'Controller';
|
||||||
|
$appClass = self::APP_NAMESPACE . $directName;
|
||||||
|
|
||||||
|
// class_exists with autoload=true is fast for already-loaded classes
|
||||||
|
if (class_exists($appClass, true)) {
|
||||||
|
return $appClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy 2: Direct match in vendor namespace
|
||||||
|
$vendorClass = self::VENDOR_NAMESPACE . $directName;
|
||||||
|
if (class_exists($vendorClass, true)) {
|
||||||
|
return $vendorClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strategy 3: Check pre-scanned available controllers (if scanned)
|
||||||
|
if (self::$scanned) {
|
||||||
if ($class = self::findInAvailable($directName)) {
|
if ($class = self::findInAvailable($directName)) {
|
||||||
return $class;
|
return $class;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Strategy 2: Folder structure (e.g., 'admin-user' → 'Admin/UserController')
|
// Strategy 4: Folder structure (e.g., 'admin-user' → 'Admin/UserController')
|
||||||
|
// Only try this for kebab-case names with multiple parts
|
||||||
$parts = explode('-', $eventPrefix);
|
$parts = explode('-', $eventPrefix);
|
||||||
if (count($parts) > 1) {
|
if (count($parts) > 1) {
|
||||||
// Try progressively deeper folder structures
|
|
||||||
// 'admin-user-settings' tries:
|
|
||||||
// - Admin/User/SettingsController
|
|
||||||
// - Admin/UserSettingsController
|
|
||||||
// - AdminUser/SettingsController
|
|
||||||
|
|
||||||
for ($folderDepth = count($parts) - 1; $folderDepth >= 1; $folderDepth--) {
|
for ($folderDepth = count($parts) - 1; $folderDepth >= 1; $folderDepth--) {
|
||||||
$folderParts = array_slice($parts, 0, $folderDepth);
|
$folderParts = array_slice($parts, 0, $folderDepth);
|
||||||
$nameParts = array_slice($parts, $folderDepth);
|
$nameParts = array_slice($parts, $folderDepth);
|
||||||
|
|
||||||
$folder = implode('/', array_map('ucfirst', $folderParts));
|
$folder = implode('\\', array_map('ucfirst', $folderParts));
|
||||||
$name = implode('', array_map('ucfirst', $nameParts)) . 'Controller';
|
$name = implode('', array_map('ucfirst', $nameParts)) . 'Controller';
|
||||||
|
|
||||||
// Try app namespace with folder
|
// Try app namespace with folder
|
||||||
$appClass = self::APP_NAMESPACE . str_replace('/', '\\', $folder) . '\\' . $name;
|
$appClass = self::APP_NAMESPACE . $folder . '\\' . $name;
|
||||||
if (class_exists($appClass)) {
|
if (class_exists($appClass, true)) {
|
||||||
self::$availableControllers[strtolower($name)] = $appClass;
|
|
||||||
return $appClass;
|
return $appClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try vendor namespace with folder
|
// Try vendor namespace with folder
|
||||||
$vendorClass = self::VENDOR_NAMESPACE . str_replace('/', '\\', $folder) . '\\' . $name;
|
$vendorClass = self::VENDOR_NAMESPACE . $folder . '\\' . $name;
|
||||||
if (class_exists($vendorClass)) {
|
if (class_exists($vendorClass, true)) {
|
||||||
self::$availableControllers[strtolower($name)] = $vendorClass;
|
|
||||||
return $vendorClass;
|
return $vendorClass;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strategy 3: Dynamic class_exists check (for newly added controllers)
|
|
||||||
$appClass = self::APP_NAMESPACE . $directName;
|
|
||||||
if (class_exists($appClass)) {
|
|
||||||
self::$availableControllers[strtolower($directName)] = $appClass;
|
|
||||||
return $appClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
$vendorClass = self::VENDOR_NAMESPACE . $directName;
|
|
||||||
if (class_exists($vendorClass)) {
|
|
||||||
self::$availableControllers[strtolower($directName)] = $vendorClass;
|
|
||||||
return $vendorClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,12 @@ class ControllerResolverTest extends TestCase
|
||||||
/** @test */
|
/** @test */
|
||||||
public function it_caches_resolved_controllers()
|
public function it_caches_resolved_controllers()
|
||||||
{
|
{
|
||||||
// First call scans and caches
|
// First call resolves and caches
|
||||||
ControllerResolver::resolve('pusher');
|
ControllerResolver::resolve('pusher');
|
||||||
|
|
||||||
$stats = ControllerResolver::getStats();
|
$stats = ControllerResolver::getStats();
|
||||||
$this->assertTrue($stats['scanned']);
|
// scanned may or may not be true (we no longer auto-scan on resolve)
|
||||||
|
// but cached should be > 0
|
||||||
$this->assertGreaterThan(0, $stats['cached']);
|
$this->assertGreaterThan(0, $stats['cached']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,8 +64,8 @@ class ControllerResolverTest extends TestCase
|
||||||
/** @test */
|
/** @test */
|
||||||
public function it_clears_cache_correctly()
|
public function it_clears_cache_correctly()
|
||||||
{
|
{
|
||||||
// Populate cache
|
// Preload to ensure scanned is true
|
||||||
ControllerResolver::resolve('pusher');
|
ControllerResolver::preload();
|
||||||
|
|
||||||
$statsBefore = ControllerResolver::getStats();
|
$statsBefore = ControllerResolver::getStats();
|
||||||
$this->assertTrue($statsBefore['scanned']);
|
$this->assertTrue($statsBefore['scanned']);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue