From 62f24eb9616f8052bb9de7529bdbacdcc3e7bc19 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 27 Apr 2026 15:08:20 -0400 Subject: [PATCH 1/3] refactor: Improve getDriverConfig() --- src/Horde.php | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/Horde.php b/src/Horde.php index dbb8fe37..2f65ab5e 100644 --- a/src/Horde.php +++ b/src/Horde.php @@ -597,41 +597,35 @@ public static function getDriverConfig(string|array $backend, ?string $type = 's { global $conf; - if ($type !== null) { + if ($type === null) { + $confType = []; + } else { $type = HordeString::lower($type); + $confType = $conf[$type] ?? []; } - if (is_array($backend)) { - $c = ArrayUtils::getElement($conf, $backend); - } elseif (isset($conf[$backend])) { - $c = $conf[$backend]; - } else { - $c = null; - } + $c = ArrayUtils::getElement($conf, $backend); - if ($c !== null && isset($c['params'])) { - $c['params']['umask'] = $conf['umask']; + if (is_array($c)) { + $params = $c['params'] ?? null; + if (is_array($params)) { + $params['umask'] = $conf['umask']; - $result = ($type !== null && isset($conf[$type])) - ? array_merge($conf[$type], $c['params']) - : $c['params']; + $result = array_merge($confType, $params); - if ((!isset($c['params']['driverconfig']) - || $c['params']['driverconfig'] != 'horde') - && $type !== null && $type === 'sql') { - if (($c['params']['protocol'] ?? null) === 'unix') { - unset($result['hostspec'], $result['port']); - } else { - unset($result['socket']); + if ($type === 'sql' && ($params['driverconfig'] ?? null) !== 'horde') { + if (($params['protocol'] ?? null) === 'unix') { + unset($result['hostspec'], $result['port']); + } else { + unset($result['socket']); + } } - } - return $result; + return $result; + } } - return ($type !== null && isset($conf[$type])) - ? $conf[$type] - : []; + return $confType; } /** From a1831959c0c4fa6d7e01cf700cbca38520d5cedb Mon Sep 17 00:00:00 2001 From: Ralf Lang Date: Wed, 13 May 2026 15:12:23 +0200 Subject: [PATCH 2/3] docs: Mark this old-style service as deprecated --- lib/Horde/Core/Factory/Twitter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Horde/Core/Factory/Twitter.php b/lib/Horde/Core/Factory/Twitter.php index e4185e7e..4bc042dd 100644 --- a/lib/Horde/Core/Factory/Twitter.php +++ b/lib/Horde/Core/Factory/Twitter.php @@ -6,6 +6,10 @@ * @author Michael J. Rubinsky * @category Horde * @package Core + * + * @deprecated Creates V1 API client using OAuth 1.0a and Horde_Controller_Request. + * A replacement based on Horde\Service\Twitter\V2 (PSR-18) and + * Horde OAuth2 is needed. */ class Horde_Core_Factory_Twitter extends Horde_Core_Factory_Injector { From acc2110aa3dc5b8d0b29c12cfc02c82078c04b73 Mon Sep 17 00:00:00 2001 From: Ralf Lang Date: Wed, 13 May 2026 15:57:18 +0200 Subject: [PATCH 3/3] refactor: Transition to Horde\Injector\Injector and a lean middleware stack Horde_Injector is a Horde\Injector\Injector and has been for a while. src/ code should type hint Horde\Injector\Injector (compatible with Horde_Injector). Reworked the controller framework - The bootstrap doesn't setup any legacy globals. horde/core now provides a helper to emit a default middleware stack with legacy globals. Routes which need them need to declare HordeCore middleware in their stack. See related PRs for explicitly declaring middleware stacks in multiple apps. https://github.com/horde/skeleton/pull/1 https://github.com/horde/base/pull/88 https://github.com/horde/content/pull/5 https://github.com/horde/jonah/pull/7 https://github.com/horde/trean/pull/4 https://github.com/horde/wicked/pull/23 https://github.com/horde/nag/pull/16 AppRouter is no longer necessary, RuntimeRoutesMapper is used to bootstrap routes from development files. Todo: Implement a PrecompiledRoutesMapper and RoutesMapperCompiler --- src/DefaultInjectorBindings.php | 7 +- src/Factory/ApiRegistryFactory.php | 4 +- src/Factory/ApplicationServiceFactory.php | 4 +- src/Factory/AuthBaseFactory.php | 4 +- src/Factory/AuthIsGlobalAdminFactory.php | 4 +- src/Factory/AuthLinkRepositoryFactory.php | 4 +- src/Factory/AuthServiceFactory.php | 16 +- src/Factory/AuthStorageFactory.php | 6 +- src/Factory/CheckCredentialsFactory.php | 4 +- src/Factory/ConfigLoaderFactory.php | 6 +- src/Factory/ConfigMetadataProviderFactory.php | 4 +- src/Factory/DbAdapterFactory.php | 4 +- src/Factory/DbServiceFactory.php | 6 +- src/Factory/DriverRepositoryFactory.php | 4 +- src/Factory/ErrorFilterFactory.php | 48 ++++ src/Factory/GroupServiceFactory.php | 6 +- src/Factory/HordeLdapServiceFactory.php | 10 +- .../IdentityHistoryRepositoryFactory.php | 4 +- src/Factory/IdentityRepositoryFactory.php | 4 +- src/Factory/IdentityServiceFactory.php | 6 +- src/Factory/LdapGroupServiceFactory.php | 6 +- src/Factory/LdapPrefsServiceFactory.php | 6 +- src/Factory/OAuthAccessTokenIssuerFactory.php | 4 +- .../OAuthAccessTokenRepositoryFactory.php | 4 +- .../OAuthAuthorizationCodeGrantFactory.php | 4 +- ...AuthAuthorizationCodeRepositoryFactory.php | 4 +- .../OAuthAuthorizationEndpointFactory.php | 4 +- src/Factory/OAuthClaimsMapperFactory.php | 4 +- .../OAuthClientAuthenticatorFactory.php | 4 +- .../OAuthClientCredentialsGrantFactory.php | 4 +- src/Factory/OAuthClientRepositoryFactory.php | 4 +- src/Factory/OAuthConsentMiddlewareFactory.php | 4 +- src/Factory/OAuthConsentRepositoryFactory.php | 4 +- src/Factory/OAuthDiscoveryEndpointFactory.php | 4 +- src/Factory/OAuthFlowStoreFactory.php | 6 +- src/Factory/OAuthHttpClientServiceFactory.php | 4 +- src/Factory/OAuthIdTokenBuilderFactory.php | 4 +- .../OAuthIntrospectionEndpointFactory.php | 4 +- src/Factory/OAuthJwksEndpointFactory.php | 4 +- .../OAuthProviderConfigRepositoryFactory.php | 4 +- src/Factory/OAuthRefreshTokenGrantFactory.php | 4 +- .../OAuthRefreshTokenIssuerFactory.php | 4 +- .../OAuthRefreshTokenRepositoryFactory.php | 4 +- .../OAuthRevocationEndpointFactory.php | 4 +- .../OAuthScopeClaimsMappingFactory.php | 4 +- src/Factory/OAuthScopeRepositoryFactory.php | 4 +- src/Factory/OAuthServerMetadataFactory.php | 4 +- src/Factory/OAuthSigningKeyFactory.php | 4 +- src/Factory/OAuthTokenEndpointFactory.php | 4 +- src/Factory/OAuthTokenRepositoryFactory.php | 4 +- src/Factory/OAuthTokenServiceFactory.php | 4 +- src/Factory/OAuthUserinfoEndpointFactory.php | 4 +- src/Factory/PermissionServiceFactory.php | 10 +- src/Factory/PrefsServiceFactory.php | 10 +- src/Factory/RegistryConfigLoaderFactory.php | 4 +- src/Factory/SecretManagerFactory.php | 4 +- src/Factory/SessionHandlerFactory.php | 14 +- src/Factory/TinymceFactory.php | 4 +- src/Factory/TinymcePageBinderFactory.php | 4 +- src/Factory/TokenServiceFactory.php | 4 +- src/Factory/TranslationManagerFactory.php | 6 +- src/InjectorBindings.php | 4 +- src/Middleware/AppRouter.php | 235 +++++------------- src/Middleware/DefaultStack.php | 32 +++ src/Middleware/HordeCore.php | 98 ++++++-- src/RampageBootstrap.php | 147 ++++++++--- src/RuntimeRoutesMapper.php | 214 ++++++++++++++++ 67 files changed, 691 insertions(+), 388 deletions(-) create mode 100644 src/Factory/ErrorFilterFactory.php create mode 100644 src/Middleware/DefaultStack.php create mode 100644 src/RuntimeRoutesMapper.php diff --git a/src/DefaultInjectorBindings.php b/src/DefaultInjectorBindings.php index a7187bbc..d47c73bf 100644 --- a/src/DefaultInjectorBindings.php +++ b/src/DefaultInjectorBindings.php @@ -33,6 +33,7 @@ use Horde\Core\Factory\DbAdapterFactory; use Horde\Core\Factory\DbServiceFactory; use Horde\Core\Factory\DriverRepositoryFactory; +use Horde\Core\Factory\ErrorFilterFactory; use Horde\Core\Factory\EventDispatcherFactory; use Horde\Core\Factory\GroupServiceFactory; use Horde\Core\Factory\HashTableFactory; @@ -80,6 +81,7 @@ use Horde\Core\Factory\TinymcePageBinderFactory; use Horde\Core\Factory\VersionServiceFactory; use Horde\Core\Middleware\AuthIsGlobalAdmin; +use Horde\Core\Middleware\ErrorFilter; use Horde\Core\Middleware\OAuthConsentMiddleware; use Horde\Core\Service\ApplicationService; use Horde\Core\Service\GroupService; @@ -142,7 +144,7 @@ use Horde\Http\ResponseFactory; use Horde\Http\StreamFactory; use Horde\Util\Variables; -use Horde_Injector; +use Horde\Injector\Injector; use Psr\EventDispatcher\EventDispatcherInterface; use Psr\EventDispatcher\ListenerProviderInterface; use Psr\Http\Client\ClientInterface as PsrHttpClientInterface; @@ -154,7 +156,7 @@ class DefaultInjectorBindings implements InjectorBindings { - public function register(Horde_Injector $injector): void + public function register(Injector $injector): void { $factories = [ 'Horde_ActiveSyncBackend' => 'Horde_Core_Factory_ActiveSyncBackend', @@ -236,6 +238,7 @@ public function register(Horde_Injector $injector): void IdTokenBuilder::class => OAuthIdTokenBuilderFactory::class, OAuthConsentMiddleware::class => OAuthConsentMiddlewareFactory::class, AuthIsGlobalAdmin::class => AuthIsGlobalAdminFactory::class, + ErrorFilter::class => ErrorFilterFactory::class, 'Horde_Service_Facebook' => 'Horde_Core_Factory_Facebook', 'Horde_Service_Twitter' => 'Horde_Core_Factory_Twitter', 'Horde_Service_UrlShortener' => 'Horde_Core_Factory_UrlShortener', diff --git a/src/Factory/ApiRegistryFactory.php b/src/Factory/ApiRegistryFactory.php index b7926063..34425c49 100644 --- a/src/Factory/ApiRegistryFactory.php +++ b/src/Factory/ApiRegistryFactory.php @@ -9,7 +9,7 @@ use Horde\Core\Config\RegistryConfigLoader; use Horde\Rpc\Dispatch\ApiProvider; use Horde\Rpc\Dispatch\MethodInvoker; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; /** @@ -20,7 +20,7 @@ */ class ApiRegistryFactory { - public function create(Horde_Injector $injector): ApiRegistry + public function create(Injector $injector): ApiRegistry { $registry = new ApiRegistry(); $registryLoader = $injector->getInstance(RegistryConfigLoader::class); diff --git a/src/Factory/ApplicationServiceFactory.php b/src/Factory/ApplicationServiceFactory.php index c5f23657..954e84f2 100644 --- a/src/Factory/ApplicationServiceFactory.php +++ b/src/Factory/ApplicationServiceFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Config\RegistryConfigLoader; use Horde\Core\Service\ApplicationService; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for ApplicationService @@ -30,7 +30,7 @@ */ class ApplicationServiceFactory { - public function create(Horde_Injector $injector): ApplicationService + public function create(Injector $injector): ApplicationService { $registryLoader = $injector->getInstance(RegistryConfigLoader::class); return new ApplicationService($registryLoader); diff --git a/src/Factory/AuthBaseFactory.php b/src/Factory/AuthBaseFactory.php index 5e1a7f86..49a31ac8 100644 --- a/src/Factory/AuthBaseFactory.php +++ b/src/Factory/AuthBaseFactory.php @@ -22,11 +22,11 @@ use Horde_Auth_Base; use Horde_Core_Factory_Injector; -use Horde_Injector; +use Horde\Injector\Injector; class AuthBaseFactory extends Horde_Core_Factory_Injector { - public function create(Horde_Injector $injector): Horde_Auth_Base + public function create(Injector $injector): Horde_Auth_Base { return $injector->getInstance('Horde_Core_Factory_Auth')->create(); } diff --git a/src/Factory/AuthIsGlobalAdminFactory.php b/src/Factory/AuthIsGlobalAdminFactory.php index fe360e77..51e2d0e9 100644 --- a/src/Factory/AuthIsGlobalAdminFactory.php +++ b/src/Factory/AuthIsGlobalAdminFactory.php @@ -17,11 +17,11 @@ use Horde\Core\Config\ConfigLoader; use Horde\Core\Middleware\AuthIsGlobalAdmin; -use Horde_Injector; +use Horde\Injector\Injector; class AuthIsGlobalAdminFactory { - public function create(Horde_Injector $injector): AuthIsGlobalAdmin + public function create(Injector $injector): AuthIsGlobalAdmin { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); diff --git a/src/Factory/AuthLinkRepositoryFactory.php b/src/Factory/AuthLinkRepositoryFactory.php index 67b14a26..abaed00a 100644 --- a/src/Factory/AuthLinkRepositoryFactory.php +++ b/src/Factory/AuthLinkRepositoryFactory.php @@ -19,7 +19,7 @@ use Horde\Db\Adapter; use Horde\Horde\Service\AuthLinkRepository; use Horde\Horde\Service\SqlAuthLinkRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; /** @@ -30,7 +30,7 @@ */ class AuthLinkRepositoryFactory { - public function create(Horde_Injector $injector): AuthLinkRepository + public function create(Injector $injector): AuthLinkRepository { try { $db = $injector->getInstance(Adapter::class); diff --git a/src/Factory/AuthServiceFactory.php b/src/Factory/AuthServiceFactory.php index 3a10448c..b121f4a9 100644 --- a/src/Factory/AuthServiceFactory.php +++ b/src/Factory/AuthServiceFactory.php @@ -29,7 +29,7 @@ use Horde\Core\Service\HordeDbService; use Horde\Imap\Client\ConnectionConfig; use Horde\Imap\Client\SecureMode; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Ldap; use RuntimeException; @@ -39,7 +39,7 @@ */ class AuthServiceFactory { - public function create(Horde_Injector $injector): AuthService + public function create(Injector $injector): AuthService { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); @@ -54,7 +54,7 @@ public function create(Horde_Injector $injector): AuthService return new AuthService($provider, $policy, $identityBridge); } - private function buildProvider(string $driver, array $params, Horde_Injector $injector): CredentialProvider + private function buildProvider(string $driver, array $params, Injector $injector): CredentialProvider { return match ($driver) { 'sql', 'auto' => $this->createSqlProvider($params, $injector), @@ -65,7 +65,7 @@ private function buildProvider(string $driver, array $params, Horde_Injector $in }; } - private function buildPolicy(array $params, Horde_Injector $injector): AccessPolicy + private function buildPolicy(array $params, Injector $injector): AccessPolicy { $loginBlock = $params['login_block'] ?? false; @@ -85,7 +85,7 @@ private function buildPolicy(array $params, Horde_Injector $injector): AccessPol return new CompoundPolicy($lockout); } - private function createSqlProvider(array $params, Horde_Injector $injector): Sql + private function createSqlProvider(array $params, Injector $injector): Sql { $dbService = $injector->getInstance(HordeDbService::class); @@ -99,7 +99,7 @@ private function createSqlProvider(array $params, Horde_Injector $injector): Sql ); } - private function createLdapProvider(array $params, Horde_Injector $injector): Ldap + private function createLdapProvider(array $params, Injector $injector): Ldap { $ldap = $injector->getInstance(Horde_Ldap::class); @@ -113,7 +113,7 @@ private function createLdapProvider(array $params, Horde_Injector $injector): Ld ); } - private function createImapProvider(array $params, Horde_Injector $injector): Imap + private function createImapProvider(array $params, Injector $injector): Imap { $hostspec = $params['hostspec'] ?? 'localhost'; $port = isset($params['port']) ? (int) $params['port'] : null; @@ -133,7 +133,7 @@ private function createImapProvider(array $params, Horde_Injector $injector): Im ); } - private function createCompositeProvider(array $params, Horde_Injector $injector): CredentialProviderRegistry + private function createCompositeProvider(array $params, Injector $injector): CredentialProviderRegistry { $providers = []; diff --git a/src/Factory/AuthStorageFactory.php b/src/Factory/AuthStorageFactory.php index 94f33937..38607730 100644 --- a/src/Factory/AuthStorageFactory.php +++ b/src/Factory/AuthStorageFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Auth\Storage\HistoryAttemptTracker; use Horde\Core\Auth\Storage\HordeLockAdapter; use Horde_History; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Lock; /** @@ -26,14 +26,14 @@ */ class AuthStorageFactory { - public function createLockManager(Horde_Injector $injector): LockManager + public function createLockManager(Injector $injector): LockManager { $lock = $injector->getInstance(Horde_Lock::class); return new HordeLockAdapter($lock); } - public function createAttemptTracker(Horde_Injector $injector): LoginAttemptTracker + public function createAttemptTracker(Injector $injector): LoginAttemptTracker { $history = $injector->getInstance(Horde_History::class); diff --git a/src/Factory/CheckCredentialsFactory.php b/src/Factory/CheckCredentialsFactory.php index aec204aa..1c7a941b 100644 --- a/src/Factory/CheckCredentialsFactory.php +++ b/src/Factory/CheckCredentialsFactory.php @@ -15,11 +15,11 @@ use Horde\Core\Auth\AuthService; use Horde\Core\Middleware\CheckCredentials; -use Horde_Injector; +use Horde\Injector\Injector; class CheckCredentialsFactory { - public function create(Horde_Injector $injector): CheckCredentials + public function create(Injector $injector): CheckCredentials { $authService = $injector->getInstance(AuthService::class); diff --git a/src/Factory/ConfigLoaderFactory.php b/src/Factory/ConfigLoaderFactory.php index 6ba2fb38..3aff8f89 100644 --- a/src/Factory/ConfigLoaderFactory.php +++ b/src/Factory/ConfigLoaderFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Config\ConfigLoader; use Horde\Core\Config\ConfigMetadataProvider; use Horde\Core\Config\Vhost; -use Horde_Injector; +use Horde\Injector\Injector; use Exception; /** @@ -37,10 +37,10 @@ class ConfigLoaderFactory /** * Create ConfigLoader instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return ConfigLoader Global config loader for all apps */ - public function create(Horde_Injector $injector): ConfigLoader + public function create(Injector $injector): ConfigLoader { // Try to get metadata provider if available $metadataProvider = null; diff --git a/src/Factory/ConfigMetadataProviderFactory.php b/src/Factory/ConfigMetadataProviderFactory.php index 4fb19fff..9b5d7393 100644 --- a/src/Factory/ConfigMetadataProviderFactory.php +++ b/src/Factory/ConfigMetadataProviderFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Config\ConfigMetadataProvider; use Horde\Core\Config\Driver\DriverRepository; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for ConfigMetadataProvider. @@ -35,7 +35,7 @@ class ConfigMetadataProviderFactory { public function __construct( - private readonly Horde_Injector $injector, + private readonly Injector $injector, ) {} /** diff --git a/src/Factory/DbAdapterFactory.php b/src/Factory/DbAdapterFactory.php index 2c09e992..2da3d4f8 100644 --- a/src/Factory/DbAdapterFactory.php +++ b/src/Factory/DbAdapterFactory.php @@ -23,7 +23,7 @@ use Horde\Db\Adapter\Pdo\Pgsql as PdoPgsql; use Horde\Db\Adapter\Pdo\Sqlite as PdoSqlite; use Horde\Db\Adapter\Oci8 as ModernOci8; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_String; use Horde_Exception; use Throwable; @@ -38,7 +38,7 @@ class DbAdapterFactory 'oci8' => ModernOci8::class, ]; - public function create(Horde_Injector $injector): Adapter + public function create(Injector $injector): Adapter { $config = Horde::getDriverConfig('', 'sql'); diff --git a/src/Factory/DbServiceFactory.php b/src/Factory/DbServiceFactory.php index efd57fb5..94aec47d 100644 --- a/src/Factory/DbServiceFactory.php +++ b/src/Factory/DbServiceFactory.php @@ -23,7 +23,7 @@ use Horde\Db\Adapter\Pdo\Mysql as PdoMysql; use Horde\Db\Adapter\Pdo\Pgsql as PdoPgsql; use Horde\Db\Adapter\Pdo\Sqlite as PdoSqlite; -use Horde_Injector; +use Horde\Injector\Injector; use InvalidArgumentException; /** @@ -52,12 +52,12 @@ class DbServiceFactory /** * Create database service from configuration * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @param string $serviceId Service identifier ('horde', 'horde:perms', etc.) * @return StandardHordeDbService Database service instance * @throws InvalidArgumentException If phptype unsupported */ - public function create(Horde_Injector $injector, string $serviceId = 'horde'): StandardHordeDbService + public function create(Injector $injector, string $serviceId = 'horde'): StandardHordeDbService { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); diff --git a/src/Factory/DriverRepositoryFactory.php b/src/Factory/DriverRepositoryFactory.php index 516325bd..ced1a4a2 100644 --- a/src/Factory/DriverRepositoryFactory.php +++ b/src/Factory/DriverRepositoryFactory.php @@ -50,7 +50,7 @@ use Horde\Core\Config\Driver\Sql\OracleDriver; use Horde\Core\Config\Driver\Sql\PostgreSQLDriver; use Horde\Core\Config\Driver\Sql\SQLiteDriver; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for DriverRepository. @@ -66,7 +66,7 @@ class DriverRepositoryFactory { public function __construct( - private readonly Horde_Injector $injector, + private readonly Injector $injector, ) {} /** diff --git a/src/Factory/ErrorFilterFactory.php b/src/Factory/ErrorFilterFactory.php new file mode 100644 index 00000000..40aa81fd --- /dev/null +++ b/src/Factory/ErrorFilterFactory.php @@ -0,0 +1,48 @@ +getInstance(ConfigLoader::class); + $conf = $configLoader->load('horde', 'conf.php'); + $admins = $conf->get('auth.admins', []); + if (!is_array($admins)) { + $admins = []; + } + } catch (Exception $e) { + // Config not available — no admins list, safe error display only + } + + return new ErrorFilter( + $admins, + new ResponseFactory(), + new StreamFactory(), + ); + } +} diff --git a/src/Factory/GroupServiceFactory.php b/src/Factory/GroupServiceFactory.php index 5eeef8b4..a8a49c4a 100644 --- a/src/Factory/GroupServiceFactory.php +++ b/src/Factory/GroupServiceFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Service\GroupService; use Horde\Core\Service\SqlGroupService; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for creating GroupService instances @@ -36,10 +36,10 @@ class GroupServiceFactory /** * Create GroupService instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return GroupService Group service instance */ - public function create(Horde_Injector $injector): GroupService + public function create(Injector $injector): GroupService { // Get the legacy Horde_Group instance (already configured via Horde_Core_Factory_Group) $groupBackend = $injector->getInstance('Horde_Group'); diff --git a/src/Factory/HordeLdapServiceFactory.php b/src/Factory/HordeLdapServiceFactory.php index 7c98e620..4cf628f5 100644 --- a/src/Factory/HordeLdapServiceFactory.php +++ b/src/Factory/HordeLdapServiceFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Service\StandardHordeLdapService; use Horde\Core\Config\ConfigLoader; use Horde_Cache; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Ldap; use Exception; use RuntimeException; @@ -50,12 +50,12 @@ class HordeLdapServiceFactory /** * Create LDAP service from configuration * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @param string $serviceId Service identifier ('horde', 'horde:groups', etc.) * @return StandardHordeLdapService LDAP service instance * @throws RuntimeException If no LDAP configuration found */ - public function create(Horde_Injector $injector, string $serviceId = 'horde'): StandardHordeLdapService + public function create(Injector $injector, string $serviceId = 'horde'): StandardHordeLdapService { $loader = $injector->getInstance(ConfigLoader::class); @@ -103,10 +103,10 @@ private function parseServiceId(string $serviceId): array * Extracted for testability - can be overridden in tests. * * @param array $ldapConfig LDAP configuration - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return Horde_Ldap LDAP adapter */ - protected function createAdapter(array $ldapConfig, Horde_Injector $injector): Horde_Ldap + protected function createAdapter(array $ldapConfig, Injector $injector): Horde_Ldap { // Add optional cache if available try { diff --git a/src/Factory/IdentityHistoryRepositoryFactory.php b/src/Factory/IdentityHistoryRepositoryFactory.php index e5224cdf..2f2421ec 100644 --- a/src/Factory/IdentityHistoryRepositoryFactory.php +++ b/src/Factory/IdentityHistoryRepositoryFactory.php @@ -20,7 +20,7 @@ use Horde\Horde\Service\SqlIdentityHistoryRepository; use Horde\Identity\IdentityHistoryRepository; use Horde\Identity\InMemoryIdentityHistoryRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; /** @@ -30,7 +30,7 @@ */ class IdentityHistoryRepositoryFactory { - public function create(Horde_Injector $injector): IdentityHistoryRepository + public function create(Injector $injector): IdentityHistoryRepository { if (class_exists(SqlIdentityHistoryRepository::class)) { try { diff --git a/src/Factory/IdentityRepositoryFactory.php b/src/Factory/IdentityRepositoryFactory.php index ee4dc54f..96bb639b 100644 --- a/src/Factory/IdentityRepositoryFactory.php +++ b/src/Factory/IdentityRepositoryFactory.php @@ -20,7 +20,7 @@ use Horde\Horde\Service\SqlIdentityRepository; use Horde\Identity\IdentityRepository; use Horde\Identity\InMemoryIdentityRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; /** @@ -30,7 +30,7 @@ */ class IdentityRepositoryFactory { - public function create(Horde_Injector $injector): IdentityRepository + public function create(Injector $injector): IdentityRepository { if (class_exists(SqlIdentityRepository::class)) { try { diff --git a/src/Factory/IdentityServiceFactory.php b/src/Factory/IdentityServiceFactory.php index 24fab4ba..c9e85cbc 100644 --- a/src/Factory/IdentityServiceFactory.php +++ b/src/Factory/IdentityServiceFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Service\IdentityService; use Horde\Core\Service\PrefsService; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for IdentityService @@ -35,10 +35,10 @@ class IdentityServiceFactory /** * Create IdentityService instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return IdentityService Identity service instance */ - public function create(Horde_Injector $injector): IdentityService + public function create(Injector $injector): IdentityService { $prefsService = $injector->getInstance(PrefsService::class); return new IdentityService($prefsService); diff --git a/src/Factory/LdapGroupServiceFactory.php b/src/Factory/LdapGroupServiceFactory.php index 004c2598..6a2643fe 100644 --- a/src/Factory/LdapGroupServiceFactory.php +++ b/src/Factory/LdapGroupServiceFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Service\LdapGroupService; use Horde\Core\Service\HordeLdapService; use Horde\Core\Config\ConfigLoader; -use Horde_Injector; +use Horde\Injector\Injector; use RuntimeException; /** @@ -35,11 +35,11 @@ class LdapGroupServiceFactory /** * Create LDAP group service from configuration * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return LdapGroupService LDAP group service instance * @throws RuntimeException If configuration invalid */ - public function create(Horde_Injector $injector): LdapGroupService + public function create(Injector $injector): LdapGroupService { $loader = $injector->getInstance(ConfigLoader::class); $config = $loader->load('horde'); diff --git a/src/Factory/LdapPrefsServiceFactory.php b/src/Factory/LdapPrefsServiceFactory.php index cf88aa56..b212c51b 100644 --- a/src/Factory/LdapPrefsServiceFactory.php +++ b/src/Factory/LdapPrefsServiceFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Service\LdapPrefsService; use Horde\Core\Service\HordeLdapService; use Horde\Core\Config\ConfigLoader; -use Horde_Injector; +use Horde\Injector\Injector; use RuntimeException; /** @@ -35,11 +35,11 @@ class LdapPrefsServiceFactory /** * Create LDAP prefs service from configuration * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return LdapPrefsService LDAP prefs service instance * @throws RuntimeException If configuration invalid */ - public function create(Horde_Injector $injector): LdapPrefsService + public function create(Injector $injector): LdapPrefsService { $loader = $injector->getInstance(ConfigLoader::class); $config = $loader->load('horde'); diff --git a/src/Factory/OAuthAccessTokenIssuerFactory.php b/src/Factory/OAuthAccessTokenIssuerFactory.php index c52f6b91..7ec839d0 100644 --- a/src/Factory/OAuthAccessTokenIssuerFactory.php +++ b/src/Factory/OAuthAccessTokenIssuerFactory.php @@ -20,11 +20,11 @@ use Horde\Jwt\TokenEncoder; use Horde\OAuth\Server\Repository\AccessTokenRepository; use Horde\OAuth\Server\Token\AccessTokenIssuer; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthAccessTokenIssuerFactory { - public function create(Horde_Injector $injector): AccessTokenIssuer + public function create(Injector $injector): AccessTokenIssuer { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthAccessTokenRepositoryFactory.php b/src/Factory/OAuthAccessTokenRepositoryFactory.php index e41732fe..95948dbe 100644 --- a/src/Factory/OAuthAccessTokenRepositoryFactory.php +++ b/src/Factory/OAuthAccessTokenRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthAccessTokenRepository; use Horde\OAuth\Server\Repository\AccessTokenRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryAccessTokenRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthAccessTokenRepositoryFactory { - public function create(Horde_Injector $injector): AccessTokenRepository + public function create(Injector $injector): AccessTokenRepository { if (class_exists(SqlOAuthAccessTokenRepository::class)) { try { diff --git a/src/Factory/OAuthAuthorizationCodeGrantFactory.php b/src/Factory/OAuthAuthorizationCodeGrantFactory.php index 6b45a91c..85594a1e 100644 --- a/src/Factory/OAuthAuthorizationCodeGrantFactory.php +++ b/src/Factory/OAuthAuthorizationCodeGrantFactory.php @@ -21,11 +21,11 @@ use Horde\OAuth\Server\Repository\ScopeRepository; use Horde\OAuth\Server\Token\AccessTokenIssuer; use Horde\OAuth\Server\Token\RefreshTokenIssuer; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthAuthorizationCodeGrantFactory { - public function create(Horde_Injector $injector): AuthorizationCodeGrant + public function create(Injector $injector): AuthorizationCodeGrant { return new AuthorizationCodeGrant( $injector->getInstance(AuthorizationCodeRepository::class), diff --git a/src/Factory/OAuthAuthorizationCodeRepositoryFactory.php b/src/Factory/OAuthAuthorizationCodeRepositoryFactory.php index 54a2adab..82e696b4 100644 --- a/src/Factory/OAuthAuthorizationCodeRepositoryFactory.php +++ b/src/Factory/OAuthAuthorizationCodeRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthAuthorizationCodeRepository; use Horde\OAuth\Server\Repository\AuthorizationCodeRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryAuthorizationCodeRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthAuthorizationCodeRepositoryFactory { - public function create(Horde_Injector $injector): AuthorizationCodeRepository + public function create(Injector $injector): AuthorizationCodeRepository { if (class_exists(SqlOAuthAuthorizationCodeRepository::class)) { try { diff --git a/src/Factory/OAuthAuthorizationEndpointFactory.php b/src/Factory/OAuthAuthorizationEndpointFactory.php index 9d979e2d..dba5ecc0 100644 --- a/src/Factory/OAuthAuthorizationEndpointFactory.php +++ b/src/Factory/OAuthAuthorizationEndpointFactory.php @@ -22,11 +22,11 @@ use Horde\OAuth\Server\Repository\ScopeRepository; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthAuthorizationEndpointFactory { - public function create(Horde_Injector $injector): AuthorizationEndpoint + public function create(Injector $injector): AuthorizationEndpoint { return new AuthorizationEndpoint( $injector->getInstance(ClientRepository::class), diff --git a/src/Factory/OAuthClaimsMapperFactory.php b/src/Factory/OAuthClaimsMapperFactory.php index 4ba847e5..6cae0466 100644 --- a/src/Factory/OAuthClaimsMapperFactory.php +++ b/src/Factory/OAuthClaimsMapperFactory.php @@ -18,11 +18,11 @@ use Horde\OAuth\Oidc\ClaimsMapper; use Horde\Horde\Service\IdentityOnlyClaimsMapper; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthClaimsMapperFactory { - public function create(Horde_Injector $injector): ClaimsMapper + public function create(Injector $injector): ClaimsMapper { return new IdentityOnlyClaimsMapper(); } diff --git a/src/Factory/OAuthClientAuthenticatorFactory.php b/src/Factory/OAuthClientAuthenticatorFactory.php index b2ef28f5..0650f7ec 100644 --- a/src/Factory/OAuthClientAuthenticatorFactory.php +++ b/src/Factory/OAuthClientAuthenticatorFactory.php @@ -20,11 +20,11 @@ use Horde\OAuth\Server\ClientAuthentication\ClientSecretBasic; use Horde\OAuth\Server\ClientAuthentication\ClientSecretPost; use Horde\OAuth\Server\Repository\ClientRepository; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthClientAuthenticatorFactory { - public function create(Horde_Injector $injector): ClientAuthenticatorChain + public function create(Injector $injector): ClientAuthenticatorChain { return new ClientAuthenticatorChain( $injector->getInstance(ClientRepository::class), diff --git a/src/Factory/OAuthClientCredentialsGrantFactory.php b/src/Factory/OAuthClientCredentialsGrantFactory.php index 5fdeffd3..e0a1e388 100644 --- a/src/Factory/OAuthClientCredentialsGrantFactory.php +++ b/src/Factory/OAuthClientCredentialsGrantFactory.php @@ -19,11 +19,11 @@ use Horde\OAuth\Server\Grant\ClientCredentialsGrant; use Horde\OAuth\Server\Repository\ScopeRepository; use Horde\OAuth\Server\Token\AccessTokenIssuer; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthClientCredentialsGrantFactory { - public function create(Horde_Injector $injector): ClientCredentialsGrant + public function create(Injector $injector): ClientCredentialsGrant { return new ClientCredentialsGrant( $injector->getInstance(AccessTokenIssuer::class), diff --git a/src/Factory/OAuthClientRepositoryFactory.php b/src/Factory/OAuthClientRepositoryFactory.php index 6db31eb3..b69a6aa3 100644 --- a/src/Factory/OAuthClientRepositoryFactory.php +++ b/src/Factory/OAuthClientRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthClientRepository; use Horde\OAuth\Server\Repository\ClientRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryClientRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthClientRepositoryFactory { - public function create(Horde_Injector $injector): ClientRepository + public function create(Injector $injector): ClientRepository { if (class_exists(SqlOAuthClientRepository::class)) { try { diff --git a/src/Factory/OAuthConsentMiddlewareFactory.php b/src/Factory/OAuthConsentMiddlewareFactory.php index e9cac7b2..5810dea2 100644 --- a/src/Factory/OAuthConsentMiddlewareFactory.php +++ b/src/Factory/OAuthConsentMiddlewareFactory.php @@ -20,7 +20,7 @@ use Horde\Core\Session\HordeSession; use Horde\OAuth\Server\Handler\AuthorizationEndpoint; use Horde\OAuth\Server\Repository\ConsentRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Notification_Handler; use Horde_PageOutput; use Psr\Http\Message\ResponseFactoryInterface; @@ -28,7 +28,7 @@ class OAuthConsentMiddlewareFactory { - public function create(Horde_Injector $injector): OAuthConsentMiddleware + public function create(Injector $injector): OAuthConsentMiddleware { return new OAuthConsentMiddleware( $injector->getInstance(AuthorizationEndpoint::class), diff --git a/src/Factory/OAuthConsentRepositoryFactory.php b/src/Factory/OAuthConsentRepositoryFactory.php index 3daec435..1bac0bfc 100644 --- a/src/Factory/OAuthConsentRepositoryFactory.php +++ b/src/Factory/OAuthConsentRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthConsentRepository; use Horde\OAuth\Server\Repository\ConsentRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryConsentRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthConsentRepositoryFactory { - public function create(Horde_Injector $injector): ConsentRepository + public function create(Injector $injector): ConsentRepository { if (class_exists(SqlOAuthConsentRepository::class)) { try { diff --git a/src/Factory/OAuthDiscoveryEndpointFactory.php b/src/Factory/OAuthDiscoveryEndpointFactory.php index 18740dd5..2b06e5de 100644 --- a/src/Factory/OAuthDiscoveryEndpointFactory.php +++ b/src/Factory/OAuthDiscoveryEndpointFactory.php @@ -20,11 +20,11 @@ use Horde\OAuth\Server\ServerMetadata; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthDiscoveryEndpointFactory { - public function create(Horde_Injector $injector): DiscoveryEndpoint + public function create(Injector $injector): DiscoveryEndpoint { return new DiscoveryEndpoint( $injector->getInstance(ServerMetadata::class), diff --git a/src/Factory/OAuthFlowStoreFactory.php b/src/Factory/OAuthFlowStoreFactory.php index 29697d79..76607e3b 100644 --- a/src/Factory/OAuthFlowStoreFactory.php +++ b/src/Factory/OAuthFlowStoreFactory.php @@ -22,11 +22,11 @@ use Horde\OAuth\Client\FileOAuthFlowStore; use Horde\OAuth\Client\OAuthFlowStore; use Horde\Horde\Service\SqlOAuthFlowStore; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthFlowStoreFactory { - public function create(Horde_Injector $injector): OAuthFlowStore + public function create(Injector $injector): OAuthFlowStore { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); @@ -48,7 +48,7 @@ private function createFileStore(array $params): FileOAuthFlowStore return new FileOAuthFlowStore($dir, $prefix); } - private function createSqlStore(Horde_Injector $injector, array $params): SqlOAuthFlowStore + private function createSqlStore(Injector $injector, array $params): SqlOAuthFlowStore { $dbService = $injector->getInstance(HordeDbService::class); $table = !empty($params['table']) ? $params['table'] : 'horde_oauth_flows'; diff --git a/src/Factory/OAuthHttpClientServiceFactory.php b/src/Factory/OAuthHttpClientServiceFactory.php index a24a131a..834b43a4 100644 --- a/src/Factory/OAuthHttpClientServiceFactory.php +++ b/src/Factory/OAuthHttpClientServiceFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Service\NullOAuthHttpClientService; use Horde\Core\Service\OAuthHttpClientService; -use Horde_Injector; +use Horde\Injector\Injector; /** * Default factory returning NullOAuthHttpClientService. @@ -28,7 +28,7 @@ */ class OAuthHttpClientServiceFactory { - public function create(Horde_Injector $injector): OAuthHttpClientService + public function create(Injector $injector): OAuthHttpClientService { return new NullOAuthHttpClientService(); } diff --git a/src/Factory/OAuthIdTokenBuilderFactory.php b/src/Factory/OAuthIdTokenBuilderFactory.php index 5bc0c47f..beb0f0cb 100644 --- a/src/Factory/OAuthIdTokenBuilderFactory.php +++ b/src/Factory/OAuthIdTokenBuilderFactory.php @@ -21,11 +21,11 @@ use Horde\OAuth\Oidc\ClaimsMapper; use Horde\OAuth\Oidc\IdTokenBuilder; use Horde\OAuth\Oidc\ScopeClaimsMapping; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthIdTokenBuilderFactory { - public function create(Horde_Injector $injector): IdTokenBuilder + public function create(Injector $injector): IdTokenBuilder { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthIntrospectionEndpointFactory.php b/src/Factory/OAuthIntrospectionEndpointFactory.php index be193ee0..4d367764 100644 --- a/src/Factory/OAuthIntrospectionEndpointFactory.php +++ b/src/Factory/OAuthIntrospectionEndpointFactory.php @@ -22,11 +22,11 @@ use Horde\OAuth\Server\Repository\RefreshTokenRepository; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthIntrospectionEndpointFactory { - public function create(Horde_Injector $injector): IntrospectionEndpoint + public function create(Injector $injector): IntrospectionEndpoint { return new IntrospectionEndpoint( $injector->getInstance(ClientAuthenticatorChain::class), diff --git a/src/Factory/OAuthJwksEndpointFactory.php b/src/Factory/OAuthJwksEndpointFactory.php index 0f30bb66..602eea86 100644 --- a/src/Factory/OAuthJwksEndpointFactory.php +++ b/src/Factory/OAuthJwksEndpointFactory.php @@ -20,11 +20,11 @@ use Horde\OAuth\Oidc\Handler\JwksEndpoint; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthJwksEndpointFactory { - public function create(Horde_Injector $injector): JwksEndpoint + public function create(Injector $injector): JwksEndpoint { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthProviderConfigRepositoryFactory.php b/src/Factory/OAuthProviderConfigRepositoryFactory.php index 9bf53692..5022c3e8 100644 --- a/src/Factory/OAuthProviderConfigRepositoryFactory.php +++ b/src/Factory/OAuthProviderConfigRepositoryFactory.php @@ -21,7 +21,7 @@ use Horde\Db\Adapter; use Horde\Horde\Service\SqlOAuthProviderConfigRepository; use Horde\Secret\SecretManager; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; /** @@ -38,7 +38,7 @@ */ class OAuthProviderConfigRepositoryFactory { - public function create(Horde_Injector $injector): OAuthProviderConfigRepository + public function create(Injector $injector): OAuthProviderConfigRepository { if (class_exists(SqlOAuthProviderConfigRepository::class)) { try { diff --git a/src/Factory/OAuthRefreshTokenGrantFactory.php b/src/Factory/OAuthRefreshTokenGrantFactory.php index e57921ac..94a9b4df 100644 --- a/src/Factory/OAuthRefreshTokenGrantFactory.php +++ b/src/Factory/OAuthRefreshTokenGrantFactory.php @@ -22,11 +22,11 @@ use Horde\OAuth\Server\Repository\ScopeRepository; use Horde\OAuth\Server\Token\AccessTokenIssuer; use Horde\OAuth\Server\Token\RefreshTokenIssuer; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthRefreshTokenGrantFactory { - public function create(Horde_Injector $injector): RefreshTokenGrant + public function create(Injector $injector): RefreshTokenGrant { return new RefreshTokenGrant( $injector->getInstance(RefreshTokenRepository::class), diff --git a/src/Factory/OAuthRefreshTokenIssuerFactory.php b/src/Factory/OAuthRefreshTokenIssuerFactory.php index 114ffe85..f27cda46 100644 --- a/src/Factory/OAuthRefreshTokenIssuerFactory.php +++ b/src/Factory/OAuthRefreshTokenIssuerFactory.php @@ -18,11 +18,11 @@ use Horde\OAuth\Server\Repository\RefreshTokenRepository; use Horde\OAuth\Server\Token\RefreshTokenIssuer; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthRefreshTokenIssuerFactory { - public function create(Horde_Injector $injector): RefreshTokenIssuer + public function create(Injector $injector): RefreshTokenIssuer { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthRefreshTokenRepositoryFactory.php b/src/Factory/OAuthRefreshTokenRepositoryFactory.php index 04085ddb..0ac5cdde 100644 --- a/src/Factory/OAuthRefreshTokenRepositoryFactory.php +++ b/src/Factory/OAuthRefreshTokenRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthRefreshTokenRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryRefreshTokenRepository; use Horde\OAuth\Server\Repository\RefreshTokenRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthRefreshTokenRepositoryFactory { - public function create(Horde_Injector $injector): RefreshTokenRepository + public function create(Injector $injector): RefreshTokenRepository { if (class_exists(SqlOAuthRefreshTokenRepository::class)) { try { diff --git a/src/Factory/OAuthRevocationEndpointFactory.php b/src/Factory/OAuthRevocationEndpointFactory.php index a13e0641..2edece7e 100644 --- a/src/Factory/OAuthRevocationEndpointFactory.php +++ b/src/Factory/OAuthRevocationEndpointFactory.php @@ -22,11 +22,11 @@ use Horde\OAuth\Server\Repository\RefreshTokenRepository; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthRevocationEndpointFactory { - public function create(Horde_Injector $injector): RevocationEndpoint + public function create(Injector $injector): RevocationEndpoint { return new RevocationEndpoint( $injector->getInstance(ClientAuthenticatorChain::class), diff --git a/src/Factory/OAuthScopeClaimsMappingFactory.php b/src/Factory/OAuthScopeClaimsMappingFactory.php index e5c30a4a..558faaae 100644 --- a/src/Factory/OAuthScopeClaimsMappingFactory.php +++ b/src/Factory/OAuthScopeClaimsMappingFactory.php @@ -17,11 +17,11 @@ namespace Horde\Core\Factory; use Horde\OAuth\Oidc\ScopeClaimsMapping; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthScopeClaimsMappingFactory { - public function create(Horde_Injector $injector): ScopeClaimsMapping + public function create(Injector $injector): ScopeClaimsMapping { return new ScopeClaimsMapping(); } diff --git a/src/Factory/OAuthScopeRepositoryFactory.php b/src/Factory/OAuthScopeRepositoryFactory.php index 02eaed22..65aedd46 100644 --- a/src/Factory/OAuthScopeRepositoryFactory.php +++ b/src/Factory/OAuthScopeRepositoryFactory.php @@ -20,12 +20,12 @@ use Horde\Horde\Service\SqlOAuthScopeRepository; use Horde\OAuth\Server\Repository\InMemory\InMemoryScopeRepository; use Horde\OAuth\Server\Repository\ScopeRepository; -use Horde_Injector; +use Horde\Injector\Injector; use Throwable; class OAuthScopeRepositoryFactory { - public function create(Horde_Injector $injector): ScopeRepository + public function create(Injector $injector): ScopeRepository { if (class_exists(SqlOAuthScopeRepository::class)) { try { diff --git a/src/Factory/OAuthServerMetadataFactory.php b/src/Factory/OAuthServerMetadataFactory.php index 59183ffa..98a3e7b0 100644 --- a/src/Factory/OAuthServerMetadataFactory.php +++ b/src/Factory/OAuthServerMetadataFactory.php @@ -17,11 +17,11 @@ namespace Horde\Core\Factory; use Horde\OAuth\Server\ServerMetadata; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthServerMetadataFactory { - public function create(Horde_Injector $injector): ServerMetadata + public function create(Injector $injector): ServerMetadata { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthSigningKeyFactory.php b/src/Factory/OAuthSigningKeyFactory.php index 0e26c379..0e08493a 100644 --- a/src/Factory/OAuthSigningKeyFactory.php +++ b/src/Factory/OAuthSigningKeyFactory.php @@ -21,12 +21,12 @@ use Horde\Jwt\Signer\Rs256Signer; use Horde\Jwt\TokenEncoder; use Horde\Horde\Service\CryptoKeyManager; -use Horde_Injector; +use Horde\Injector\Injector; use Horde\Exception\HordeRuntimeException; class OAuthSigningKeyFactory { - public function create(Horde_Injector $injector): PrivateKey + public function create(Injector $injector): PrivateKey { $conf = $GLOBALS['conf'] ?? []; $oauthConf = $conf['oauth_server'] ?? []; diff --git a/src/Factory/OAuthTokenEndpointFactory.php b/src/Factory/OAuthTokenEndpointFactory.php index 9ee42324..4b0b9156 100644 --- a/src/Factory/OAuthTokenEndpointFactory.php +++ b/src/Factory/OAuthTokenEndpointFactory.php @@ -23,11 +23,11 @@ use Horde\OAuth\Server\Handler\TokenEndpoint; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthTokenEndpointFactory { - public function create(Horde_Injector $injector): TokenEndpoint + public function create(Injector $injector): TokenEndpoint { return new TokenEndpoint( $injector->getInstance(ClientAuthenticatorChain::class), diff --git a/src/Factory/OAuthTokenRepositoryFactory.php b/src/Factory/OAuthTokenRepositoryFactory.php index 615d6d75..b584a90f 100644 --- a/src/Factory/OAuthTokenRepositoryFactory.php +++ b/src/Factory/OAuthTokenRepositoryFactory.php @@ -19,7 +19,7 @@ use Horde\Core\Config\ConfigLoader; use Horde\Core\Service\NullOAuthTokenRepository; use Horde\Core\Service\OAuthTokenRepository; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for OAuthTokenRepository. @@ -34,7 +34,7 @@ */ class OAuthTokenRepositoryFactory { - public function create(Horde_Injector $injector): OAuthTokenRepository + public function create(Injector $injector): OAuthTokenRepository { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); diff --git a/src/Factory/OAuthTokenServiceFactory.php b/src/Factory/OAuthTokenServiceFactory.php index ae526046..f0755fbe 100644 --- a/src/Factory/OAuthTokenServiceFactory.php +++ b/src/Factory/OAuthTokenServiceFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Service\NullOAuthTokenService; use Horde\Core\Service\OAuthTokenService; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for OAuthTokenService. @@ -34,7 +34,7 @@ */ class OAuthTokenServiceFactory { - public function create(Horde_Injector $injector): OAuthTokenService + public function create(Injector $injector): OAuthTokenService { return new NullOAuthTokenService(); } diff --git a/src/Factory/OAuthUserinfoEndpointFactory.php b/src/Factory/OAuthUserinfoEndpointFactory.php index 8d1f4ac5..ebed7265 100644 --- a/src/Factory/OAuthUserinfoEndpointFactory.php +++ b/src/Factory/OAuthUserinfoEndpointFactory.php @@ -21,11 +21,11 @@ use Horde\OAuth\Oidc\ScopeClaimsMapping; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; -use Horde_Injector; +use Horde\Injector\Injector; class OAuthUserinfoEndpointFactory { - public function create(Horde_Injector $injector): UserinfoEndpoint + public function create(Injector $injector): UserinfoEndpoint { return new UserinfoEndpoint( $injector->getInstance(ClaimsMapper::class), diff --git a/src/Factory/PermissionServiceFactory.php b/src/Factory/PermissionServiceFactory.php index 3cbc366b..338306c3 100644 --- a/src/Factory/PermissionServiceFactory.php +++ b/src/Factory/PermissionServiceFactory.php @@ -23,7 +23,7 @@ use Horde\Core\Config\ConfigLoader; use Horde_Perms_Sql; use Horde_Perms_Null; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Cache; use Horde_Log_Logger; use RuntimeException; @@ -47,11 +47,11 @@ class PermissionServiceFactory /** * Create PermissionService instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return PermissionService Permission service with configured backend * @throws RuntimeException If driver unsupported */ - public function create(Horde_Injector $injector): PermissionService + public function create(Injector $injector): PermissionService { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); @@ -73,12 +73,12 @@ public function create(Horde_Injector $injector): PermissionService * - System-wide horde connection (driverconfig='horde') * - Service-specific connection (custom DB params) * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @param array $params Permission configuration parameters * @return SqlPermissionService SQL permission service */ private function createSqlBackend( - Horde_Injector $injector, + Injector $injector, array $params ): SqlPermissionService { // Get database connection via DbServiceFactory with pooling diff --git a/src/Factory/PrefsServiceFactory.php b/src/Factory/PrefsServiceFactory.php index d3543379..beae3c28 100644 --- a/src/Factory/PrefsServiceFactory.php +++ b/src/Factory/PrefsServiceFactory.php @@ -21,7 +21,7 @@ use Horde\Core\Service\SqlPrefsService; use Horde\Core\Service\NullPrefsService; use Horde\Core\Service\HordeDbService; -use Horde_Injector; +use Horde\Injector\Injector; use RuntimeException; /** @@ -87,11 +87,11 @@ class PrefsServiceFactory /** * Create PrefsService instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return PrefsService Prefs service instance * @throws RuntimeException If driver unsupported */ - public function create(Horde_Injector $injector): PrefsService + public function create(Injector $injector): PrefsService { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); @@ -109,11 +109,11 @@ public function create(Horde_Injector $injector): PrefsService /** * Create SQL prefs backend * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @param array $params Prefs configuration parameters * @return SqlPrefsService SQL prefs service */ - private function createSqlBackend(Horde_Injector $injector, array $params): SqlPrefsService + private function createSqlBackend(Injector $injector, array $params): SqlPrefsService { // Get DB service (supports 'horde:prefs' pattern in future) $dbService = $injector->getInstance(HordeDbService::class); diff --git a/src/Factory/RegistryConfigLoaderFactory.php b/src/Factory/RegistryConfigLoaderFactory.php index 6faee671..46bccd64 100644 --- a/src/Factory/RegistryConfigLoaderFactory.php +++ b/src/Factory/RegistryConfigLoaderFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Config\RegistryConfigLoader; use Horde\Core\Config\Vhost; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for RegistryConfigLoader @@ -30,7 +30,7 @@ */ class RegistryConfigLoaderFactory { - public function create(Horde_Injector $injector): RegistryConfigLoader + public function create(Injector $injector): RegistryConfigLoader { $configBase = defined('HORDE_CONFIG_BASE') ? HORDE_CONFIG_BASE : '/etc/horde'; $vendorBase = defined('HORDE_BASE') ? HORDE_BASE : __DIR__ . '/../../../'; diff --git a/src/Factory/SecretManagerFactory.php b/src/Factory/SecretManagerFactory.php index 341db7da..ab3d6d1a 100644 --- a/src/Factory/SecretManagerFactory.php +++ b/src/Factory/SecretManagerFactory.php @@ -18,7 +18,7 @@ use Horde\Core\Config\ConfigLoader; use Horde\Secret\SecretManager; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for SecretManager. @@ -33,7 +33,7 @@ */ class SecretManagerFactory { - public function create(Horde_Injector $injector): SecretManager + public function create(Injector $injector): SecretManager { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); diff --git a/src/Factory/SessionHandlerFactory.php b/src/Factory/SessionHandlerFactory.php index 534e1dc1..12f559e1 100644 --- a/src/Factory/SessionHandlerFactory.php +++ b/src/Factory/SessionHandlerFactory.php @@ -28,7 +28,7 @@ use Horde\SessionHandler\Storage\StackBackend; use Horde_HashTable_Base; use Horde_HashTable_Lock; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Secret; use Psr\EventDispatcher\EventDispatcherInterface; use RuntimeException; @@ -53,11 +53,11 @@ class SessionHandlerFactory /** * Create SessionHandler instance * - * @param Horde_Injector $injector Dependency injector + * @param Injector $injector Dependency injector * @return SessionHandler Configured session handler * @throws RuntimeException If driver is unsupported */ - public function create(Horde_Injector $injector): SessionHandler + public function create(Injector $injector): SessionHandler { $loader = $injector->getInstance(ConfigLoader::class); $state = $loader->load('horde'); @@ -104,7 +104,7 @@ private function createBuiltinBackend(array $params): BuiltinBackend /** * @param array $params */ - private function createSqlBackend(Horde_Injector $injector, array $params): SqlBackend + private function createSqlBackend(Injector $injector, array $params): SqlBackend { $dbService = $injector->getInstance(HordeDbService::class); $db = $dbService->getAdapter(); @@ -119,7 +119,7 @@ private function createSqlBackend(Horde_Injector $injector, array $params): SqlB /** * @param array $params */ - private function createHashtableBackend(Horde_Injector $injector, array $params): HashtableBackend + private function createHashtableBackend(Injector $injector, array $params): HashtableBackend { $ht = $injector->getInstance('Horde_HashTable'); @@ -166,7 +166,7 @@ private function shouldStack(State $state, string $driver): bool || (bool) $state->get('sessionhandler.memcache', false); } - private function createSessionFactory(Horde_Injector $injector): HordeSessionFactory + private function createSessionFactory(Injector $injector): HordeSessionFactory { $encryptor = null; $decryptor = null; @@ -187,7 +187,7 @@ private function createSessionFactory(Horde_Injector $injector): HordeSessionFac ); } - private function getEventDispatcher(Horde_Injector $injector): ?EventDispatcherInterface + private function getEventDispatcher(Injector $injector): ?EventDispatcherInterface { try { $dispatcher = $injector->getInstance(EventDispatcherInterface::class); diff --git a/src/Factory/TinymceFactory.php b/src/Factory/TinymceFactory.php index aecc8fbd..f2867b4d 100644 --- a/src/Factory/TinymceFactory.php +++ b/src/Factory/TinymceFactory.php @@ -16,7 +16,7 @@ namespace Horde\Core\Factory; use Horde\Editor\Tinymce; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for Tinymce editor driver. @@ -27,7 +27,7 @@ */ class TinymceFactory { - public function create(Horde_Injector $injector): Tinymce + public function create(Injector $injector): Tinymce { return new Tinymce(); } diff --git a/src/Factory/TinymcePageBinderFactory.php b/src/Factory/TinymcePageBinderFactory.php index 56e1079e..a468533f 100644 --- a/src/Factory/TinymcePageBinderFactory.php +++ b/src/Factory/TinymcePageBinderFactory.php @@ -17,7 +17,7 @@ use Horde\Core\Editor\TinymcePageBinder; use Horde\Editor\Tinymce; -use Horde_Injector; +use Horde\Injector\Injector; /** * Factory for TinymcePageBinder. @@ -28,7 +28,7 @@ */ class TinymcePageBinderFactory { - public function create(Horde_Injector $injector): TinymcePageBinder + public function create(Injector $injector): TinymcePageBinder { return new TinymcePageBinder( $injector->getInstance(Tinymce::class), diff --git a/src/Factory/TokenServiceFactory.php b/src/Factory/TokenServiceFactory.php index d6dab7d2..685f46ab 100644 --- a/src/Factory/TokenServiceFactory.php +++ b/src/Factory/TokenServiceFactory.php @@ -19,7 +19,7 @@ use Horde; use Horde\Token\Token; use Horde\Token\TokenConfig; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_String; use Horde_Support_Randomid; @@ -41,7 +41,7 @@ */ class TokenServiceFactory { - public function create(Horde_Injector $injector): Token + public function create(Injector $injector): Token { global $conf, $session; diff --git a/src/Factory/TranslationManagerFactory.php b/src/Factory/TranslationManagerFactory.php index 2bef7107..c00b502c 100644 --- a/src/Factory/TranslationManagerFactory.php +++ b/src/Factory/TranslationManagerFactory.php @@ -17,7 +17,7 @@ namespace Horde\Core\Factory; use Horde\Core\Translation\TranslationManager; -use Horde_Injector; +use Horde\Injector\Injector; use Horde_Registry; use Exception; @@ -38,11 +38,11 @@ class TranslationManagerFactory /** * Create and preload a TranslationManager. * - * @param Horde_Injector $injector The dependency injector. + * @param Injector $injector The dependency injector. * * @return TranslationManager Preloaded with all app domains. */ - public function create(Horde_Injector $injector): TranslationManager + public function create(Injector $injector): TranslationManager { $manager = new TranslationManager(); $registry = $injector->getInstance(Horde_Registry::class); diff --git a/src/InjectorBindings.php b/src/InjectorBindings.php index d0d66ef1..5d852c4b 100644 --- a/src/InjectorBindings.php +++ b/src/InjectorBindings.php @@ -16,9 +16,9 @@ namespace Horde\Core; -use Horde_Injector; +use Horde\Injector\Injector; interface InjectorBindings { - public function register(Horde_Injector $injector): void; + public function register(Injector $injector): void; } diff --git a/src/Middleware/AppRouter.php b/src/Middleware/AppRouter.php index db5ae3fe..c09abe71 100644 --- a/src/Middleware/AppRouter.php +++ b/src/Middleware/AppRouter.php @@ -5,229 +5,111 @@ namespace Horde\Core\Middleware; use Exception; -use Horde; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\StreamFactoryInterface; use Horde\Http\Server\RampageRequestHandler; -use Horde_Registry; -use Horde_Application; -use Horde_Controller; -use Horde_Injector; -use Horde\Routes\Mapper; -use Horde\Routes\Matcher; +use Horde\Http\ResponseFactory; +use Horde\Http\StreamFactory; +use Horde\Injector\Injector; use Horde\Routes\MatchResult; +use Horde_Controller; use Horde_String; use Psr\Http\Message\ResponseFactoryInterface; use Horde\Exception\HordeException; -use Horde\Core\Config\ConfigLoader; -use Horde\Core\Config\BackendConfigLoader; -use Horde\Core\Config\PrefsConfigLoader; -use Horde\Core\Config\RegistryConfigLoader; -use Horde\Core\Config\Vhost; +use Horde\Core\RuntimeRoutesMapper; /** * AppRouter middleware * - * Purpose: - * - * Run the router for the app from the attribute - * Retrieve the route specific stack - * If no route found, present a helpful but security-wise acceptable response - * - * Requires Attributes: - * - app - * - prefix + * Matches the request against the pre-loaded RuntimeRoutesMapper, + * resolves the per-route middleware stack, and dispatches the controller. * * Sets Attributes: - * - * + * - app + * - matchResult + * - route */ class AppRouter extends RampageRequestHandler implements MiddlewareInterface, RequestHandlerInterface { - private Mapper $mapper; - private Horde_Registry $registry; - private Horde_Injector $injector; - - public function __construct(Horde_Registry $registry, Mapper $mapper, Horde_Injector $injector) - { - $this->registry = $registry; - $this->mapper = $mapper; - $this->injector = $injector; + public function __construct( + private readonly RuntimeRoutesMapper $runtimeMapper, + private readonly Injector $injector, + ) { } - /** - * Route a request for a horde app - * - * Depends on the AppFinder running first - * This middleware really only works with the Rampage Runner - * - * @param ServerRequestInterface $request - * @param RequestHandlerInterface $handler - * - * @return ResponseInterface - */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - // Setup ConfigLoader in DI container if not already present - if (!$this->injector->has(ConfigLoader::class)) { - if (!defined('HORDE_CONFIG_BASE')) { - throw new Exception('HORDE_CONFIG_BASE not defined'); - } - $this->injector->setInstance( - ConfigLoader::class, - new ConfigLoader(HORDE_CONFIG_BASE, new Vhost()) // Auto-detect vhost - ); - } - - // Setup BackendConfigLoader in DI container if not already present - if (!$this->injector->has(BackendConfigLoader::class)) { - if (!defined('HORDE_CONFIG_BASE')) { - throw new Exception('HORDE_CONFIG_BASE not defined'); - } - // Vendor base = composer vendor/horde/ directory - $vendorBase = dirname(__DIR__, 4) . '/vendor/horde'; - $this->injector->setInstance( - BackendConfigLoader::class, - new BackendConfigLoader(HORDE_CONFIG_BASE, $vendorBase, new Vhost()) // Auto-detect vhost - ); - } - - // Setup PrefsConfigLoader in DI container if not already present - if (!$this->injector->has(PrefsConfigLoader::class)) { - $vendorBase = dirname(__DIR__, 4) . '/vendor/horde'; - $this->injector->setInstance( - PrefsConfigLoader::class, - new PrefsConfigLoader(HORDE_CONFIG_BASE, $vendorBase, new Vhost()) // Auto-detect vhost - ); - } + $path = $request->getUri()->getPath(); + $result = $this->runtimeMapper->routematch($path); - // Setup RegistryConfigLoader in DI container if not already present - if (!$this->injector->has(RegistryConfigLoader::class)) { - // Vendor base = horde base app directory - $vendorBase = dirname(__DIR__, 4) . '/vendor/horde/horde'; - $this->injector->setInstance( - RegistryConfigLoader::class, - new RegistryConfigLoader(HORDE_CONFIG_BASE, $vendorBase, new Vhost()) // Auto-detect vhost - ); - } - - $app = $request->getAttribute('app'); - $prefix = $request->getAttribute('routerPrefix'); - if (is_null($prefix)) { - throw new Exception("Missing Attribute: 'routerPrefix'"); - } - if (empty($app)) { - throw new Exception("Missing Attribute: 'app'"); - } - $defaultStack = [ - AuthHordeSession::class, - RedirectToLogin::class, - ]; - - // Check for route definitions. - $fileroot = $this->registry->get('fileroot', $app); - $routeFile = $fileroot . '/config/routes.php'; - if (!file_exists($routeFile)) { - throw new Exception("No Routes file found for App $app"); - } - - // TODO: Should this move to another middleware? - - // Before PushApp, we need to load the Horde Autoloader - // Push $app onto the registry - $this->registry->pushApp($app); - - // Application routes are relative only to the application. Let the - // mapper know where they start. - $this->mapper->prefix = $prefix; - - // Load application routes. - // Cannot rename mapper as long as we support the existing routes definitions - $mapper = $router = $this->mapper; - include $routeFile; - if (file_exists($fileroot . '/config/routes.local.php')) { - include $fileroot . '/config/routes.local.php'; + if ($result === null) { + $responseFactory = new ResponseFactory(); + $streamFactory = new StreamFactory(); + return $responseFactory->createResponse(404) + ->withBody($streamFactory->createStream('No route matched: ' . $path)); } - // Match using PSR-7 Matcher (auto-populates environ from request) - // @TODO Cache routes - $matcher = new Matcher($this->mapper, $request); - $matchResult = $matcher->getMatchResult(); - - // Build plain array for backward compatibility - $match = $matchResult !== null ? $matchResult->toArray() : []; + [$matchDict, $route] = $result; + $app = $matchDict['app']; + $routeName = $route->routeName ?? ''; + $matchResult = new MatchResult($matchDict, $route, $routeName); - // Set the typed MatchResult as a request attribute + $request = $request->withAttribute('app', $app); $request = $request->withAttribute('matchResult', $matchResult); - // Backward compat: also set the plain array as 'route' - $request = $request->withAttribute('route', $match); + $request = $request->withAttribute('route', $matchDict); - // Bind MatchResult in injector so controllers can type-hint it - if ($matchResult !== null) { - $this->injector->setInstance(MatchResult::class, $matchResult); - } + $this->injector->setInstance(MatchResult::class, $matchResult); - // compatibility: if unset stack and HordeAuthType is 'NONE' set empty stack - if (!isset($match['stack']) && ($match['HordeAuthType'] ?? null) === 'NONE') { - $match['stack'] = []; + // Resolve middleware stack + $defaultStack = DefaultStack::get(); + if (!isset($matchDict['stack']) && ($matchDict['HordeAuthType'] ?? null) === 'NONE') { + $matchDict['stack'] = []; } - // Stack is an array of DI keys - // Empty array means NO more middleware besides controller - // unset stack means DEFAULT middleware stack - $stack = $match['stack'] ?? $defaultStack; + $stack = $matchDict['stack'] ?? $defaultStack; // DEBUG - Only log if HORDE_DEBUG_ROUTER is set if (getenv('HORDE_DEBUG_ROUTER')) { - // Use web-writable var directory - $varDir = dirname($fileroot, 2) . '/var'; - if (!is_dir($varDir)) { - $varDir = '/tmp'; - } + $varDir = '/tmp'; $debugLog = $varDir . '/approuter-debug.log'; file_put_contents($debugLog, "=== AppRouter Debug ===\n", FILE_APPEND); file_put_contents($debugLog, 'Time: ' . date('Y-m-d H:i:s') . "\n", FILE_APPEND); - file_put_contents($debugLog, 'Route: ' . ($match['name'] ?? 'UNNAMED') . "\n", FILE_APPEND); - file_put_contents($debugLog, 'Controller: ' . ($match['controller'] ?? 'NONE') . "\n", FILE_APPEND); - file_put_contents($debugLog, 'HordeAuthType: ' . ($match['HordeAuthType'] ?? 'NOT SET') . "\n", FILE_APPEND); - file_put_contents($debugLog, 'stack isset: ' . (isset($match['stack']) ? 'YES' : 'NO') . "\n", FILE_APPEND); - file_put_contents($debugLog, 'stack value: ' . json_encode($match['stack'] ?? 'NOT SET') . "\n", FILE_APPEND); + file_put_contents($debugLog, 'App: ' . $app . "\n", FILE_APPEND); + file_put_contents($debugLog, 'Route: ' . $routeName . "\n", FILE_APPEND); + file_put_contents($debugLog, 'Controller: ' . ($matchDict['controller'] ?? 'NONE') . "\n", FILE_APPEND); file_put_contents($debugLog, 'Using stack: ' . json_encode($stack) . "\n", FILE_APPEND); - file_put_contents($debugLog, 'Stack count: ' . count($stack) . "\n", FILE_APPEND); + file_put_contents($debugLog, "=== End AppRouter Debug ===\n\n", FILE_APPEND); } - foreach ($stack as $middleware) { - if (getenv('HORDE_DEBUG_ROUTER')) { - file_put_contents($debugLog, 'Adding middleware: ' . $middleware . "\n", FILE_APPEND); + foreach ($stack as $mw) { + if ($mw === HordeCore::class) { + // HordeCore is a takeover middleware — it does appInit, then + // resolves the remaining stack from the legacy injector itself. + // Pass the full stack and controller info via request attributes + // so HordeCore knows what to dispatch downstream. + $remaining = array_slice($stack, array_search(HordeCore::class, $stack) + 1); + $request = $request->withAttribute('_hordecore_remaining_stack', $remaining); + $request = $request->withAttribute('_hordecore_controller', $matchDict['controller'] ?? ''); + $handler->addMiddleware($this->injector->get($mw)); + return $handler->handle($request); } - $handler->addMiddleware($this->injector->get($middleware)); + $handler->addMiddleware($this->injector->get($mw)); } - if (getenv('HORDE_DEBUG_ROUTER')) { - file_put_contents($debugLog, "=== End AppRouter Debug ===\n\n", FILE_APPEND); - } - - // Controller is a single DI key for either a HandlerInterface, MiddlewareInterface or a Horde_Controller - $controllerName = $match['controller'] ?? ''; - $traditionalFilename = $fileroot . '/app/controllers/' . $controllerName . '.php'; + // Resolve controller (only reached for stacks without HordeCore) + $controllerName = $matchDict['controller'] ?? ''; $controller = null; if ($controllerName) { try { $controller = $this->injector->getInstance($controllerName); } catch (Exception $e) { - if (empty($controller)) { - if (file_exists($traditionalFilename)) { - require_once $traditionalFilename; - $traditionalName = Horde_String::ucfirst($app) . '_' . Horde_String::ucfirst($controllerName) . '_Controller'; - if ($this->injector->hasInstance($traditionalName) || class_exists($traditionalName)) { - $controller = $this->injector->getInstance($traditionalName); - } - } else { - throw new HordeException('Defined controller but could not create: ' . $controllerName . ' — ' . $e->getMessage(), 0, $e); - } - } + throw new HordeException( + 'Defined controller but could not create: ' . $controllerName . ' — ' . $e->getMessage(), + 0, + $e + ); } } @@ -246,10 +128,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } // Controllers can be implemented as a Payload RequestHandler if ($controller instanceof RequestHandlerInterface) { - // Set controller as a payload handler - // Simply calling controller would bypass any further middleware $handler->setPayloadHandler($controller); } + return $handler->handle($request); } } diff --git a/src/Middleware/DefaultStack.php b/src/Middleware/DefaultStack.php new file mode 100644 index 00000000..49751170 --- /dev/null +++ b/src/Middleware/DefaultStack.php @@ -0,0 +1,32 @@ +withMiddleware(DefaultStack::get()) + * rather than relying on implicit fallback behavior in AppRouter. + */ +class DefaultStack +{ + public static function get(): array + { + return [ + HordeCore::class, + ErrorFilter::class, + AuthHordeSession::class, + RedirectToLogin::class, + ]; + } +} diff --git a/src/Middleware/HordeCore.php b/src/Middleware/HordeCore.php index 7879d391..a797219c 100644 --- a/src/Middleware/HordeCore.php +++ b/src/Middleware/HordeCore.php @@ -4,48 +4,49 @@ namespace Horde\Core\Middleware; +use Exception; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Horde_Registry; -use Horde_Application; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\ServerRequestFactoryInterface; -use Horde\Core\Config\RegistryState; use Horde\Http\RequestFactory; use Horde\Http\UriFactory; use Horde\Http\StreamFactory; use Horde\Http\ResponseFactory; -use Horde\Routes\Mapper; +use Horde\Http\Server\RampageRequestHandler; +use Horde\Exception\HordeException; +use Horde_Controller; +use Horde_Registry; +use Horde_Application; use Horde_Exception; /** - * HordeCoreMiddleware - * - * Sets up a Horde Application Framework environment - * Initially one long process, should progressively be split into multiple middlewares + * HordeCore middleware — takeover middleware for legacy routes * + * Initializes the full Horde Application Framework environment, then + * resolves and dispatches the remaining middleware stack and controller + * from the legacy $GLOBALS['injector']. Never calls the outer handler. */ class HordeCore implements MiddlewareInterface { public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { - // run AppInit, implicitly load core - // Using ::class would defeat the purpose here if (!class_exists('Horde_Application')) { throw new Horde_Exception('Autoloading issue'); } if (!class_exists('Horde_Registry')) { throw new Horde_Exception('Autoloading issue'); } - // This does way too much - $hordeEnv = Horde_Registry::appInit('horde', ['authentication' => 'none']); - // Bad! the injector should be part of the early init's response. + + // Initialize legacy Horde environment (globals, session, conf, etc.) + Horde_Registry::appInit('horde', ['authentication' => 'none']); $injector = $GLOBALS['injector']; - // If there are no existing implementations, set them + $injector->setInstance('Horde_Injector', $injector); + if (!$injector->has(UriFactoryInterface::class)) { $injector->setInstance(UriFactoryInterface::class, new UriFactory()); } @@ -60,18 +61,63 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } $registry = $injector->getInstance('Horde_Registry'); - $admins = $GLOBALS['conf']['auth']['admins'] ?? []; - $errorFilter = new ErrorFilter($admins, new ResponseFactory(), new StreamFactory()); - $injector->setInstance(ErrorFilter::class, $errorFilter); - // First middleware should be ErrorFilter to catch all errors - $handler->addMiddleware($errorFilter); - // Detect correct app - $registryState = new RegistryState($registry->applications); - $handler->addMiddleware(new AppFinder($registryState, new ResponseFactory(), new StreamFactory())); - // Find route inside detected app - $handler->addMiddleware(new AppRouter($registry, $injector->get(Mapper::class), $injector)); - $request = $request->withAttribute('registry', $registry); - return $handler->handle($request); + + // Bridge RuntimeRoutesMapper into legacy injector so controllers can use urlFor() + $mapper = $request->getAttribute('mapper'); + if ($mapper !== null) { + $injector->setInstance(\Horde\Routes\Mapper::class, $mapper); + $injector->setInstance(\Horde\Core\RuntimeRoutesMapper::class, $mapper); + } + + // Push the identified app onto the legacy registry stack + $app = $request->getAttribute('app'); + if ($app) { + $registry->pushApp($app); + } + + // Compute remaining stack (everything after HordeCore) + $fullStack = $request->getAttribute('stack', []); + $myPosition = array_search(self::class, $fullStack, true); + $remainingStack = ($myPosition !== false) + ? array_slice($fullStack, $myPosition + 1) + : []; + + $controllerName = $request->getAttribute('controller', ''); + + // Build internal pipeline — resolve from legacy injector + $responseFactory = $injector->get(ResponseFactoryInterface::class); + $streamFactory = $injector->get(StreamFactoryInterface::class); + $internalHandler = new RampageRequestHandler($responseFactory, $streamFactory); + + foreach ($remainingStack as $mw) { + $internalHandler->addMiddleware($injector->get($mw)); + } + + // Resolve controller + if ($controllerName) { + try { + $controller = $injector->getInstance($controllerName); + } catch (Exception $e) { + throw new HordeException( + 'Defined controller but could not create: ' . $controllerName . ' — ' . $e->getMessage(), + 0, + $e + ); + } + + if ($controller instanceof Horde_Controller) { + $middleware = new H5Controller($controller, $responseFactory, $streamFactory); + $internalHandler->addMiddleware($middleware); + } + if ($controller instanceof MiddlewareInterface) { + $internalHandler->addMiddleware($controller); + } + if ($controller instanceof RequestHandlerInterface) { + $internalHandler->setPayloadHandler($controller); + } + } + + return $internalHandler->handle($request); } } diff --git a/src/RampageBootstrap.php b/src/RampageBootstrap.php index b866b207..2c9f7f47 100644 --- a/src/RampageBootstrap.php +++ b/src/RampageBootstrap.php @@ -1,56 +1,135 @@ setInstance(Injector::class, $injector); + $injector->setInstance(\Horde_Injector::class, $injector); + + if (class_exists('Horde\Bundle\PrecompiledBindings')) { + (new \Horde\Bundle\PrecompiledBindings())->register($injector); + } else { + (new DefaultInjectorBindings())->register($injector); + } + + // 2. Build ServerRequest + $request = (new RequestBuilder())->withGlobalVariables()->build(); + $injector->setInstance(ServerRequestInterface::class, $request); + + // 3. Ensure HORDE_CONFIG_BASE is defined + if (!defined('HORDE_CONFIG_BASE')) { + define('HORDE_CONFIG_BASE', InstalledVersions::getRootPackage()['install_path'] . '/var/config'); + } + + // 4. Load RegistryState + if (class_exists('Horde\Bundle\PrecompiledRegistry')) { + $registryState = new \Horde\Bundle\PrecompiledRegistry(); + } else { + $vendorBase = defined('HORDE_BASE') + ? HORDE_BASE + : InstalledVersions::getRootPackage()['install_path']; + $registryState = (new RegistryConfigLoader(HORDE_CONFIG_BASE, $vendorBase, new Vhost()))->load(); + } + $injector->setInstance(RegistryState::class, $registryState); + + // 5. RuntimeRoutesMapper — pre-load ALL app routes + $runtimeMapper = new RuntimeRoutesMapper($registryState, $request); + $runtimeMapper->loadAllApps(); + $injector->setInstance(RuntimeRoutesMapper::class, $runtimeMapper); + $injector->setInstance(\Horde\Routes\Mapper::class, $runtimeMapper); + + // 6. Match route + $path = $request->getUri()->getPath(); + $result = $runtimeMapper->routematch($path); + + if ($result === null) { + http_response_code(404); + echo 'No route matched: ' . htmlspecialchars($path); + return; + } + + [$matchDict, $route] = $result; + $app = $matchDict['app']; + $matchResult = new MatchResult($matchDict, $route, $route->routeName ?? ''); + $injector->setInstance(MatchResult::class, $matchResult); + + // Determine middleware stack and controller + if (!isset($matchDict['stack']) && ($matchDict['HordeAuthType'] ?? null) === 'NONE') { + $matchDict['stack'] = []; + } + $stack = $matchDict['stack'] ?? []; + $controller = $matchDict['controller'] ?? ''; + + // Set request attributes — formal contract with middlewares + $request = $request->withAttribute('app', $app); + $request = $request->withAttribute('matchResult', $matchResult); + $request = $request->withAttribute('route', $matchDict); + $request = $request->withAttribute('stack', $stack); + $request = $request->withAttribute('controller', $controller); + $request = $request->withAttribute('mapper', $runtimeMapper); + + // 7. Build handler — stack as class names, resolved lazily via injector + $handler = new RampageRequestHandler( + $responseFactory, + $streamFactory, + $stack, + $controller !== '' ? $controller : null, + $injector, + ); - // Build the request from server variables. - // The RequestBuilder could easily be autowired by a DI container. - $requestBuilder = new RequestBuilder($requestFactory, $streamFactory, $uriFactory); - $request = $requestBuilder->withGlobalVariables()->build(); - $injector = new Horde_Injector_TopLevel(); - $middlewares = [ - // JWT Session middleware - runs BEFORE HordeCoreMiddleware - // Sets custom session ID from JWT refresh token if present - // Use NullLogger as logger not yet available in DI container - new JwtSession(new NullLogger()), - // TODO: Unconditionally setup the output compressor, it should act only upon an attribute - // TODO: Unconditionally setup the error handler - // Setup the horde init middleware. It will add more middleware to the stack - new HordeCoreMiddleware(), - ]; - - $handler = new RampageRequestHandler($responseFactory, $streamFactory, $middlewares); + // 8. Run $runner = new Runner($handler, new ResponseWriterWeb()); - $runner->run($request); + try { + $runner->run($request); + } catch (Throwable $e) { + http_response_code(500); + error_log((string) $e); + if (getenv('HORDE_DEBUG')) { + echo '
' . htmlspecialchars((string) $e) . '
'; + } else { + echo 'Internal Server Error'; + } + } } } diff --git a/src/RuntimeRoutesMapper.php b/src/RuntimeRoutesMapper.php new file mode 100644 index 00000000..7cc2367c --- /dev/null +++ b/src/RuntimeRoutesMapper.php @@ -0,0 +1,214 @@ +getUri(); + $host = $uri->getHost(); + $port = $uri->getPort(); + $this->environ['HTTP_HOST'] = $port ? $host . ':' . $port : $host; + $this->environ['SERVER_NAME'] = $host; + $this->environ['REQUEST_METHOD'] = $request->getMethod(); + $scheme = $uri->getScheme(); + if ($scheme === 'https') { + $this->environ['HTTPS'] = 'on'; + } + } + + /** + * Override buildRoute to inject the current app's base URI into each route. + */ + public function buildRoute(?string $uri = null, ?string $name = null): FluentRouteBuilder + { + $fullUri = $this->currentAppPrefix . ($uri ?? ''); + $builder = parent::buildRoute($fullUri, $name); + + if ($this->currentAppHost !== null) { + $builder->withHost($this->currentAppHost); + } + if ($this->currentAppScheme !== null) { + $builder->withScheme($this->currentAppScheme); + } + if ($this->currentAppPort !== null) { + $builder->withPort($this->currentAppPort); + } + + $builder->withDefaults(['app' => $this->currentApp]); + + return $builder; + } + + /** + * Override connect to inject the current app's base URI into legacy routes. + */ + public function connect($first, $second = null, $third = null) + { + // Replicate parent's arg parsing to find routePath and kargs + if ($third !== null) { + $routeName = $first; + $routePath = $second; + $kargs = $third; + } elseif ($second !== null) { + if (is_array($second)) { + $routeName = null; + $routePath = $first; + $kargs = $second; + } else { + $routeName = $first; + $routePath = $second; + $kargs = []; + } + } else { + $routeName = null; + $routePath = $first; + $kargs = []; + } + + // Inject app's base URI + $routePath = $this->currentAppPrefix . $routePath; + $kargs['app'] = $kargs['app'] ?? $this->currentApp; + + if ($this->currentAppHost !== null && !isset($kargs['_host'])) { + $kargs['_host'] = $this->currentAppHost; + } + if ($this->currentAppScheme !== null && !isset($kargs['_scheme'])) { + $kargs['_scheme'] = $this->currentAppScheme; + } + if ($this->currentAppPort !== null && !isset($kargs['_port'])) { + $kargs['_port'] = $this->currentAppPort; + } + + // Call parent with normalized 3-arg form + if ($routeName !== null) { + parent::connect($routeName, $routePath, $kargs); + } else { + parent::connect($routePath, $kargs); + } + } + + /** + * Load routes from all active apps in RegistryState. + */ + public function loadAllApps(): void + { + $configBase = defined('HORDE_CONFIG_BASE') ? HORDE_CONFIG_BASE : ''; + + foreach ($this->registryState->toArray() as $app => $config) { + $status = $config['status'] ?? 'active'; + if (!in_array($status, ['active', 'notoolbar', 'hidden', 'admin'])) { + continue; + } + + $fileroot = $config['fileroot'] ?? null; + $webroot = $config['webroot'] ?? null; + if (!$fileroot || !$webroot) { + continue; + } + + $routeFile = $fileroot . '/config/routes.php'; + if (!file_exists($routeFile)) { + continue; + } + + // Parse webroot into components + $parsed = $this->parseWebroot($webroot); + $this->currentAppPrefix = $parsed['path']; + $this->currentAppHost = $parsed['host']; + $this->currentAppScheme = $parsed['scheme']; + $this->currentAppPort = $parsed['port']; + $this->currentApp = $app; + + // routes.php calls $mapper->buildRoute() / $mapper->connect() + $mapper = $this; + include $routeFile; + + // Local overrides + if ($configBase !== '') { + $localRouteFile = $configBase . '/' . $app . '/routes.local.php'; + if (file_exists($localRouteFile)) { + include $localRouteFile; + } + } + + $inAppLocal = $fileroot . '/config/routes.local.php'; + if (file_exists($inAppLocal)) { + include $inAppLocal; + } + } + + // Reset state + $this->currentAppPrefix = ''; + $this->currentAppHost = null; + $this->currentAppScheme = null; + $this->currentAppPort = null; + $this->currentApp = ''; + + // Build route regexps once + $this->createRegs(); + } + + /** + * Parse a webroot (path-only or full URL) into components. + * + * @return array{path: string, host: ?string, scheme: ?string, port: ?int} + */ + private function parseWebroot(string $webroot): array + { + if (str_starts_with($webroot, 'http://') || str_starts_with($webroot, 'https://')) { + $parsed = parse_url($webroot); + return [ + 'path' => rtrim($parsed['path'] ?? '', '/'), + 'host' => $parsed['host'] ?? null, + 'scheme' => $parsed['scheme'] ?? null, + 'port' => isset($parsed['port']) ? (int) $parsed['port'] : null, + ]; + } + + return [ + 'path' => rtrim($webroot, '/'), + 'host' => null, + 'scheme' => null, + 'port' => null, + ]; + } +}