blob: 0c3574f13710c9d99cc27109d626c8170e710895 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace LdapRecord;
4
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01005use LDAP\Connection as RawLdapConnection;
6
7/** @psalm-suppress UndefinedClass */
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02008class Ldap implements LdapInterface
9{
10 use HandlesConnection, DetectsErrors;
11
12 /**
13 * @inheritdoc
14 */
15 public function getEntries($searchResults)
16 {
17 return $this->executeFailableOperation(function () use ($searchResults) {
18 return ldap_get_entries($this->connection, $searchResults);
19 });
20 }
21
22 /**
23 * Retrieves the first entry from a search result.
24 *
25 * @see http://php.net/manual/en/function.ldap-first-entry.php
26 *
27 * @param resource $searchResults
28 *
29 * @return resource
30 */
31 public function getFirstEntry($searchResults)
32 {
33 return $this->executeFailableOperation(function () use ($searchResults) {
34 return ldap_first_entry($this->connection, $searchResults);
35 });
36 }
37
38 /**
39 * Retrieves the next entry from a search result.
40 *
41 * @see http://php.net/manual/en/function.ldap-next-entry.php
42 *
43 * @param resource $entry
44 *
45 * @return resource
46 */
47 public function getNextEntry($entry)
48 {
49 return $this->executeFailableOperation(function () use ($entry) {
50 return ldap_next_entry($this->connection, $entry);
51 });
52 }
53
54 /**
55 * Retrieves the ldap entry's attributes.
56 *
57 * @see http://php.net/manual/en/function.ldap-get-attributes.php
58 *
59 * @param resource $entry
60 *
61 * @return array|false
62 */
63 public function getAttributes($entry)
64 {
65 return $this->executeFailableOperation(function () use ($entry) {
66 return ldap_get_attributes($this->connection, $entry);
67 });
68 }
69
70 /**
71 * Returns the number of entries from a search result.
72 *
73 * @see http://php.net/manual/en/function.ldap-count-entries.php
74 *
75 * @param resource $searchResults
76 *
77 * @return int
78 */
79 public function countEntries($searchResults)
80 {
81 return $this->executeFailableOperation(function () use ($searchResults) {
82 return ldap_count_entries($this->connection, $searchResults);
83 });
84 }
85
86 /**
87 * Compare value of attribute found in entry specified with DN.
88 *
89 * @see http://php.net/manual/en/function.ldap-compare.php
90 *
91 * @param string $dn
92 * @param string $attribute
93 * @param string $value
94 *
95 * @return mixed
96 */
97 public function compare($dn, $attribute, $value)
98 {
99 return $this->executeFailableOperation(function () use ($dn, $attribute, $value) {
100 return ldap_compare($this->connection, $dn, $attribute, $value);
101 });
102 }
103
104 /**
105 * @inheritdoc
106 */
107 public function getLastError()
108 {
109 if (! $this->connection) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100110 return null;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200111 }
112
113 return ldap_error($this->connection);
114 }
115
116 /**
117 * @inheritdoc
118 */
119 public function getDetailedError()
120 {
121 if (! $number = $this->errNo()) {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100122 return null;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200123 }
124
125 $this->getOption(LDAP_OPT_DIAGNOSTIC_MESSAGE, $message);
126
127 return new DetailedError($number, $this->err2Str($number), $message);
128 }
129
130 /**
131 * Get all binary values from the specified result entry.
132 *
133 * @see http://php.net/manual/en/function.ldap-get-values-len.php
134 *
135 * @param $entry
136 * @param $attribute
137 *
138 * @return array
139 */
140 public function getValuesLen($entry, $attribute)
141 {
142 return $this->executeFailableOperation(function () use ($entry, $attribute) {
143 return ldap_get_values_len($this->connection, $entry, $attribute);
144 });
145 }
146
147 /**
148 * @inheritdoc
149 */
150 public function setOption($option, $value)
151 {
152 return ldap_set_option($this->connection, $option, $value);
153 }
154
155 /**
156 * @inheritdoc
157 */
158 public function getOption($option, &$value = null)
159 {
160 ldap_get_option($this->connection, $option, $value);
161
162 return $value;
163 }
164
165 /**
166 * Set a callback function to do re-binds on referral chasing.
167 *
168 * @see http://php.net/manual/en/function.ldap-set-rebind-proc.php
169 *
170 * @param callable $callback
171 *
172 * @return bool
173 */
174 public function setRebindCallback(callable $callback)
175 {
176 return ldap_set_rebind_proc($this->connection, $callback);
177 }
178
179 /**
180 * @inheritdoc
181 */
182 public function startTLS()
183 {
184 return $this->executeFailableOperation(function () {
185 return ldap_start_tls($this->connection);
186 });
187 }
188
189 /**
190 * @inheritdoc
191 */
192 public function connect($hosts = [], $port = 389)
193 {
194 $this->bound = false;
195
196 $this->host = $this->makeConnectionUris($hosts, $port);
197
198 return $this->connection = $this->executeFailableOperation(function () {
199 return ldap_connect($this->host);
200 });
201 }
202
203 /**
204 * @inheritdoc
205 */
206 public function close()
207 {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100208 $result = (is_resource($this->connection) || $this->connection instanceof RawLdapConnection)
209 ? @ldap_close($this->connection)
210 : false;
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200211
212 $this->connection = null;
213 $this->bound = false;
214 $this->host = null;
215
216 return $result;
217 }
218
219 /**
220 * @inheritdoc
221 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100222 public function search($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = [])
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200223 {
224 return $this->executeFailableOperation(function () use (
225 $dn,
226 $filter,
227 $fields,
228 $onlyAttributes,
229 $size,
230 $time,
231 $deref,
232 $serverControls
233 ) {
234 return empty($serverControls)
235 ? ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref)
236 : ldap_search($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls);
237 });
238 }
239
240 /**
241 * @inheritdoc
242 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100243 public function listing($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = [])
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200244 {
245 return $this->executeFailableOperation(function () use (
246 $dn,
247 $filter,
248 $fields,
249 $onlyAttributes,
250 $size,
251 $time,
252 $deref,
253 $serverControls
254 ) {
255 return empty($serverControls)
256 ? ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref)
257 : ldap_list($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls);
258 });
259 }
260
261 /**
262 * @inheritdoc
263 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100264 public function read($dn, $filter, array $fields, $onlyAttributes = false, $size = 0, $time = 0, $deref = LDAP_DEREF_NEVER, $serverControls = [])
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200265 {
266 return $this->executeFailableOperation(function () use (
267 $dn,
268 $filter,
269 $fields,
270 $onlyAttributes,
271 $size,
272 $time,
273 $deref,
274 $serverControls
275 ) {
276 return empty($serverControls)
277 ? ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref)
278 : ldap_read($this->connection, $dn, $filter, $fields, $onlyAttributes, $size, $time, $deref, $serverControls);
279 });
280 }
281
282 /**
283 * @inheritdoc
284 */
285 public function parseResult($result, &$errorCode, &$dn, &$errorMessage, &$referrals, &$serverControls = [])
286 {
287 return $this->executeFailableOperation(function () use (
288 $result,
289 &$errorCode,
290 &$dn,
291 &$errorMessage,
292 &$referrals,
293 &$serverControls
294 ) {
295 return empty($serverControls)
296 ? ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals)
297 : ldap_parse_result($this->connection, $result, $errorCode, $dn, $errorMessage, $referrals, $serverControls);
298 });
299 }
300
301 /**
302 * @inheritdoc
303 */
304 public function bind($username, $password)
305 {
306 return $this->bound = $this->executeFailableOperation(function () use ($username, $password) {
307 return ldap_bind($this->connection, $username, html_entity_decode($password));
308 });
309 }
310
311 /**
312 * @inheritdoc
313 */
314 public function add($dn, array $entry)
315 {
316 return $this->executeFailableOperation(function () use ($dn, $entry) {
317 return ldap_add($this->connection, $dn, $entry);
318 });
319 }
320
321 /**
322 * @inheritdoc
323 */
324 public function delete($dn)
325 {
326 return $this->executeFailableOperation(function () use ($dn) {
327 return ldap_delete($this->connection, $dn);
328 });
329 }
330
331 /**
332 * @inheritdoc
333 */
334 public function rename($dn, $newRdn, $newParent, $deleteOldRdn = false)
335 {
336 return $this->executeFailableOperation(function () use (
337 $dn,
338 $newRdn,
339 $newParent,
340 $deleteOldRdn
341 ) {
342 return ldap_rename($this->connection, $dn, $newRdn, $newParent, $deleteOldRdn);
343 });
344 }
345
346 /**
347 * @inheritdoc
348 */
349 public function modify($dn, array $entry)
350 {
351 return $this->executeFailableOperation(function () use ($dn, $entry) {
352 return ldap_modify($this->connection, $dn, $entry);
353 });
354 }
355
356 /**
357 * @inheritdoc
358 */
359 public function modifyBatch($dn, array $values)
360 {
361 return $this->executeFailableOperation(function () use ($dn, $values) {
362 return ldap_modify_batch($this->connection, $dn, $values);
363 });
364 }
365
366 /**
367 * @inheritdoc
368 */
369 public function modAdd($dn, array $entry)
370 {
371 return $this->executeFailableOperation(function () use ($dn, $entry) {
372 return ldap_mod_add($this->connection, $dn, $entry);
373 });
374 }
375
376 /**
377 * @inheritdoc
378 */
379 public function modReplace($dn, array $entry)
380 {
381 return $this->executeFailableOperation(function () use ($dn, $entry) {
382 return ldap_mod_replace($this->connection, $dn, $entry);
383 });
384 }
385
386 /**
387 * @inheritdoc
388 */
389 public function modDelete($dn, array $entry)
390 {
391 return $this->executeFailableOperation(function () use ($dn, $entry) {
392 return ldap_mod_del($this->connection, $dn, $entry);
393 });
394 }
395
396 /**
397 * @inheritdoc
398 */
399 public function controlPagedResult($pageSize = 1000, $isCritical = false, $cookie = '')
400 {
401 return $this->executeFailableOperation(function () use ($pageSize, $isCritical, $cookie) {
402 return ldap_control_paged_result($this->connection, $pageSize, $isCritical, $cookie);
403 });
404 }
405
406 /**
407 * @inheritdoc
408 */
409 public function controlPagedResultResponse($result, &$cookie, &$estimated = null)
410 {
411 return $this->executeFailableOperation(function () use ($result, &$cookie, &$estimated) {
412 return ldap_control_paged_result_response($this->connection, $result, $cookie, $estimated);
413 });
414 }
415
416 /**
417 * @inheritdoc
418 */
419 public function freeResult($result)
420 {
421 return ldap_free_result($result);
422 }
423
424 /**
425 * @inheritdoc
426 */
427 public function errNo()
428 {
429 return $this->connection ? ldap_errno($this->connection) : null;
430 }
431
432 /**
433 * @inheritdoc
434 */
435 public function err2Str($number)
436 {
437 return ldap_err2str($number);
438 }
439
440 /**
441 * Returns the extended error hex code of the last command.
442 *
443 * @return string|null
444 */
445 public function getExtendedErrorHex()
446 {
447 if (preg_match("/(?<=data\s).*?(?=,)/", $this->getExtendedError(), $code)) {
448 return $code[0];
449 }
450 }
451
452 /**
453 * Returns the extended error code of the last command.
454 *
455 * @return bool|string
456 */
457 public function getExtendedErrorCode()
458 {
459 return $this->extractDiagnosticCode($this->getExtendedError());
460 }
461
462 /**
463 * Extract the diagnostic code from the message.
464 *
465 * @param string $message
466 *
467 * @return string|bool
468 */
469 public function extractDiagnosticCode($message)
470 {
471 preg_match('/^([\da-fA-F]+):/', $message, $matches);
472
473 return isset($matches[1]) ? $matches[1] : false;
474 }
475
476 /**
477 * @inheritdoc
478 */
479 public function getDiagnosticMessage()
480 {
481 $this->getOption(LDAP_OPT_ERROR_STRING, $message);
482
483 return $message;
484 }
485}