[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <[email protected]> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Symfony\Component\Ldap\Adapter\ExtLdap; 13 14 use LDAP\Connection as LDAPConnection; 15 use LDAP\Result; 16 use Symfony\Component\Ldap\Adapter\AbstractQuery; 17 use Symfony\Component\Ldap\Exception\LdapException; 18 use Symfony\Component\Ldap\Exception\NotBoundException; 19 20 /** 21 * @author Charles Sarrazin <[email protected]> 22 * @author Bob van de Vijver <[email protected]> 23 */ 24 class Query extends AbstractQuery 25 { 26 // As of PHP 7.2, we can use LDAP_CONTROL_PAGEDRESULTS instead of this 27 public const PAGINATION_OID = '1.2.840.113556.1.4.319'; 28 29 /** @var Connection */ 30 protected $connection; 31 32 /** @var resource[]|Result[] */ 33 private $results; 34 35 /** @var array */ 36 private $serverctrls = []; 37 38 public function __construct(Connection $connection, string $dn, string $query, array $options = []) 39 { 40 parent::__construct($connection, $dn, $query, $options); 41 } 42 43 /** 44 * @return array 45 */ 46 public function __sleep() 47 { 48 throw new \BadMethodCallException('Cannot serialize '.__CLASS__); 49 } 50 51 public function __wakeup() 52 { 53 throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); 54 } 55 56 public function __destruct() 57 { 58 $con = $this->connection->getResource(); 59 $this->connection = null; 60 61 if (null === $this->results) { 62 return; 63 } 64 65 foreach ($this->results as $result) { 66 if (false === $result || null === $result) { 67 continue; 68 } 69 if (!ldap_free_result($result)) { 70 throw new LdapException('Could not free results: '.ldap_error($con)); 71 } 72 } 73 $this->results = null; 74 } 75 76 /** 77 * {@inheritdoc} 78 */ 79 public function execute() 80 { 81 if (null === $this->results) { 82 // If the connection is not bound, throw an exception. Users should use an explicit bind call first. 83 if (!$this->connection->isBound()) { 84 throw new NotBoundException('Query execution is not possible without binding the connection first.'); 85 } 86 87 $this->results = []; 88 $con = $this->connection->getResource(); 89 90 switch ($this->options['scope']) { 91 case static::SCOPE_BASE: 92 $func = 'ldap_read'; 93 break; 94 case static::SCOPE_ONE: 95 $func = 'ldap_list'; 96 break; 97 case static::SCOPE_SUB: 98 $func = 'ldap_search'; 99 break; 100 default: 101 throw new LdapException(sprintf('Could not search in scope "%s".', $this->options['scope'])); 102 } 103 104 $itemsLeft = $maxItems = $this->options['maxItems']; 105 $pageSize = $this->options['pageSize']; 106 // Deal with the logic to handle maxItems properly. If we can satisfy it in 107 // one request based on pageSize, we don't need to bother sending page control 108 // to the server so that it can determine what we already know. 109 if (0 !== $maxItems && $pageSize > $maxItems) { 110 $pageSize = 0; 111 } elseif (0 !== $maxItems) { 112 $pageSize = min($maxItems, $pageSize); 113 } 114 $pageControl = $this->options['scope'] != static::SCOPE_BASE && $pageSize > 0; 115 $cookie = ''; 116 do { 117 if ($pageControl) { 118 $this->controlPagedResult($con, $pageSize, true, $cookie); 119 } 120 $sizeLimit = $itemsLeft; 121 if ($pageSize > 0 && $sizeLimit >= $pageSize) { 122 $sizeLimit = 0; 123 } 124 $search = $this->callSearchFunction($con, $func, $sizeLimit); 125 126 if (false === $search) { 127 $ldapError = ''; 128 if ($errno = ldap_errno($con)) { 129 $ldapError = sprintf(' LDAP error was [%d] %s', $errno, ldap_error($con)); 130 } 131 if ($pageControl) { 132 $this->resetPagination(); 133 } 134 135 throw new LdapException(sprintf('Could not complete search with dn "%s", query "%s" and filters "%s".%s.', $this->dn, $this->query, implode(',', $this->options['filter']), $ldapError)); 136 } 137 138 $this->results[] = $search; 139 $itemsLeft -= min($itemsLeft, $pageSize); 140 141 if (0 !== $maxItems && 0 === $itemsLeft) { 142 break; 143 } 144 if ($pageControl) { 145 $cookie = $this->controlPagedResultResponse($con, $search, $cookie); 146 } 147 } while (null !== $cookie && '' !== $cookie); 148 149 if ($pageControl) { 150 $this->resetPagination(); 151 } 152 } 153 154 return new Collection($this->connection, $this); 155 } 156 157 /** 158 * Returns an LDAP search resource. If this query resulted in multiple searches, only the first 159 * page will be returned. 160 * 161 * @return resource|Result|null 162 * 163 * @internal 164 */ 165 public function getResource(int $idx = 0) 166 { 167 return $this->results[$idx] ?? null; 168 } 169 170 /** 171 * Returns all LDAP search resources. 172 * 173 * @return resource[]|Result[] 174 * 175 * @internal 176 */ 177 public function getResources(): array 178 { 179 return $this->results; 180 } 181 182 /** 183 * Resets pagination on the current connection. 184 */ 185 private function resetPagination() 186 { 187 $con = $this->connection->getResource(); 188 $this->controlPagedResult($con, 0, false, ''); 189 $this->serverctrls = []; 190 191 // This is a workaround for a bit of a bug in the above invocation 192 // of ldap_control_paged_result. Instead of indicating to extldap that 193 // we no longer wish to page queries on this link, this invocation sets 194 // the LDAP_CONTROL_PAGEDRESULTS OID with a page size of 0. This isn't 195 // well defined by RFC 2696 if there is no cookie present, so some servers 196 // will interpret it differently and do the wrong thing. Forcefully remove 197 // the OID for now until a fix can make its way through the versions of PHP 198 // the we support. 199 // 200 // This is not supported in PHP < 7.2, so these versions will remain broken. 201 $ctl = []; 202 ldap_get_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl); 203 if (!empty($ctl)) { 204 foreach ($ctl as $idx => $info) { 205 if (static::PAGINATION_OID == $info['oid']) { 206 unset($ctl[$idx]); 207 } 208 } 209 ldap_set_option($con, \LDAP_OPT_SERVER_CONTROLS, $ctl); 210 } 211 } 212 213 /** 214 * Sets LDAP pagination controls. 215 * 216 * @param resource|LDAPConnection $con 217 */ 218 private function controlPagedResult($con, int $pageSize, bool $critical, string $cookie): bool 219 { 220 if (\PHP_VERSION_ID < 70300) { 221 return ldap_control_paged_result($con, $pageSize, $critical, $cookie); 222 } 223 $this->serverctrls = [ 224 [ 225 'oid' => \LDAP_CONTROL_PAGEDRESULTS, 226 'isCritical' => $critical, 227 'value' => [ 228 'size' => $pageSize, 229 'cookie' => $cookie, 230 ], 231 ], 232 ]; 233 234 return true; 235 } 236 237 /** 238 * Retrieve LDAP pagination cookie. 239 * 240 * @param resource|LDAPConnection $con 241 * @param resource|Result $result 242 */ 243 private function controlPagedResultResponse($con, $result, string $cookie = ''): string 244 { 245 if (\PHP_VERSION_ID < 70300) { 246 ldap_control_paged_result_response($con, $result, $cookie); 247 248 return $cookie; 249 } 250 ldap_parse_result($con, $result, $errcode, $matcheddn, $errmsg, $referrals, $controls); 251 252 return $controls[\LDAP_CONTROL_PAGEDRESULTS]['value']['cookie'] ?? ''; 253 } 254 255 /** 256 * Calls actual LDAP search function with the prepared options and parameters. 257 * 258 * @param resource|LDAPConnection $con 259 * 260 * @return resource|Result|false 261 */ 262 private function callSearchFunction($con, callable $func, int $sizeLimit) 263 { 264 if (\PHP_VERSION_ID < 70300) { 265 return @$func($con, $this->dn, $this->query, $this->options['filter'], $this->options['attrsOnly'], $sizeLimit, $this->options['timeout'], $this->options['deref']); 266 } 267 268 return @$func($con, $this->dn, $this->query, $this->options['filter'], $this->options['attrsOnly'], $sizeLimit, $this->options['timeout'], $this->options['deref'], $this->serverctrls); 269 } 270 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |