1<?php 2 3/** 4 * This file is part of the FreeDSx LDAP package. 5 * 6 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace FreeDSx\Ldap\Server\RequestHandler; 13 14use FreeDSx\Ldap\Exception\RuntimeException; 15use FreeDSx\Ldap\Server\HandlerFactoryInterface; 16use Throwable; 17 18/** 19 * This is used by the server protocol handler to instantiate the possible user-land LDAP handlers (ie. handlers exposed 20 * in the public API options). 21 * 22 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 23 */ 24class HandlerFactory implements HandlerFactoryInterface 25{ 26 /** 27 * @var RequestHandlerInterface|null 28 */ 29 private $requestHandler; 30 31 /** 32 * @var RootDseHandlerInterface|null 33 */ 34 private $rootdseHandler; 35 36 /** 37 * @var PagingHandlerInterface|null 38 */ 39 private $pagingHandler; 40 41 /** 42 * @var array<string, mixed> 43 */ 44 private $options; 45 46 /** 47 * @param array<string, mixed> $options 48 */ 49 public function __construct(array $options) 50 { 51 $this->options = $options; 52 } 53 54 /** 55 * @inheritDoc 56 */ 57 public function makeRequestHandler(): RequestHandlerInterface 58 { 59 if (!$this->requestHandler) { 60 $requestHandler = !isset($this->options['request_handler']) 61 ? new GenericRequestHandler() 62 : $this->makeOrReturnInstanceOf( 63 'request_handler', 64 RequestHandlerInterface::class 65 ); 66 if (!$requestHandler instanceof RequestHandlerInterface) { 67 throw new RuntimeException(sprintf( 68 'Expected an instance of %s, got: %s', 69 RequestHandlerInterface::class, 70 get_class($requestHandler) 71 )); 72 } 73 $this->requestHandler = $requestHandler; 74 } 75 76 return $this->requestHandler; 77 } 78 79 /** 80 * @inheritDoc 81 */ 82 public function makeRootDseHandler(): ?RootDseHandlerInterface 83 { 84 if ($this->rootdseHandler) { 85 return $this->rootdseHandler; 86 } 87 $handler = $this->makeRequestHandler(); 88 $this->rootdseHandler = $handler instanceof RootDseHandlerInterface 89 ? $handler 90 : null; 91 92 if ($this->rootdseHandler) { 93 return $this->rootdseHandler; 94 } 95 96 if (isset($this->options['rootdse_handler'])) { 97 $handler = $this->makeOrReturnInstanceOf( 98 'rootdse_handler', 99 RootDseHandlerInterface::class 100 ); 101 } 102 103 if ($handler instanceof RootDseHandlerInterface) { 104 $this->rootdseHandler = $handler; 105 } 106 107 return $this->rootdseHandler; 108 } 109 110 /** 111 * @inheritDoc 112 */ 113 public function makePagingHandler(): ?PagingHandlerInterface 114 { 115 if ($this->pagingHandler) { 116 return $this->pagingHandler; 117 } 118 119 $handler = null; 120 if (isset($this->options['paging_handler'])) { 121 $handler = $this->makeOrReturnInstanceOf( 122 'paging_handler', 123 PagingHandlerInterface::class 124 ); 125 } 126 127 if ($handler !== null && !$handler instanceof PagingHandlerInterface) { 128 throw new RuntimeException(sprintf( 129 'Expected an instance of %s, got: %s', 130 PagingHandlerInterface::class, 131 get_class($handler) 132 )); 133 } 134 $this->pagingHandler = $handler; 135 136 return $this->pagingHandler; 137 } 138 139 /** 140 * @param string $optionName 141 * @param class-string $class 142 * @return object 143 */ 144 private function makeOrReturnInstanceOf( 145 string $optionName, 146 string $class 147 ) { 148 if (!isset($this->options[$optionName])) { 149 throw new RuntimeException(sprintf( 150 'Option "%s" must be an instance of or fully qualified class-name implementing "%s".', 151 $optionName, 152 $class 153 )); 154 } 155 156 $objOrString = $this->options[$optionName]; 157 if (!(is_object($objOrString) || is_string($objOrString))) { 158 throw new RuntimeException(sprintf( 159 'Option "%s" must be an instance of or fully qualified class-name implementing "%s".', 160 $optionName, 161 $class 162 )); 163 } 164 165 if (is_object($objOrString) && is_subclass_of($objOrString, $class)) { 166 return $objOrString; 167 } 168 169 if (is_string($objOrString) && is_subclass_of($objOrString, $class)) { 170 try { 171 return new $objOrString(); 172 } catch (Throwable $e) { 173 throw new RuntimeException(sprintf( 174 'Unable to instantiate class "%s" for option "%s": %s', 175 $objOrString, 176 $optionName, 177 $e->getMessage() 178 ), $e->getCode(), $e); 179 } 180 } 181 182 throw new RuntimeException(sprintf( 183 'Option "%s" must be an instance of or fully qualified class-name implementing "%s".', 184 $optionName, 185 $class 186 )); 187 } 188} 189