blob: 41334b6890d004b0730af93b6441117c3f6a15a1 [file] [log] [blame]
<?php
namespace LdapRecord;
use Closure;
use ErrorException;
use Exception;
trait HandlesConnection
{
/**
* The LDAP host that is currently connected.
*
* @var string|null
*/
protected $host;
/**
* The LDAP connection resource.
*
* @var resource|null
*/
protected $connection;
/**
* The bound status of the connection.
*
* @var bool
*/
protected $bound = false;
/**
* Whether the connection must be bound over SSL.
*
* @var bool
*/
protected $useSSL = false;
/**
* Whether the connection must be bound over TLS.
*
* @var bool
*/
protected $useTLS = false;
/**
* @inheritdoc
*/
public function isUsingSSL()
{
return $this->useSSL;
}
/**
* @inheritdoc
*/
public function isUsingTLS()
{
return $this->useTLS;
}
/**
* @inheritdoc
*/
public function isBound()
{
return $this->bound;
}
/**
* @inheritdoc
*/
public function isConnected()
{
return ! is_null($this->connection);
}
/**
* @inheritdoc
*/
public function canChangePasswords()
{
return $this->isUsingSSL() || $this->isUsingTLS();
}
/**
* @inheritdoc
*/
public function ssl($enabled = true)
{
$this->useSSL = $enabled;
return $this;
}
/**
* @inheritdoc
*/
public function tls($enabled = true)
{
$this->useTLS = $enabled;
return $this;
}
/**
* @inheritdoc
*/
public function setOptions(array $options = [])
{
foreach ($options as $option => $value) {
$this->setOption($option, $value);
}
}
/**
* @inheritdoc
*/
public function getHost()
{
return $this->host;
}
/**
* @inheritdoc
*/
public function getConnection()
{
return $this->connection;
}
/**
* @inheritdoc
*/
public function getProtocol()
{
return $this->isUsingSSL() ? LdapInterface::PROTOCOL_SSL : LdapInterface::PROTOCOL;
}
/**
* @inheritdoc
*/
public function getExtendedError()
{
return $this->getDiagnosticMessage();
}
/**
* Convert warnings to exceptions for the given operation.
*
* @param Closure $operation
*
* @throws LdapRecordException
*
* @return mixed
*/
protected function executeFailableOperation(Closure $operation)
{
// If some older versions of PHP, errors are reported instead of throwing
// exceptions, which could be a signifcant detriment to our application.
// Here, we will enforce these operations to throw exceptions instead.
set_error_handler(function ($severity, $message, $file, $line) {
if (! $this->shouldBypassError($message)) {
throw new ErrorException($message, $severity, $severity, $file, $line);
}
});
try {
if (($result = $operation()) !== false) {
return $result;
}
// If the failed query operation was a based on a query being executed
// -- such as a search, read, or listing, then we can safely return
// the failed response here and prevent throwning an exception.
if ($this->shouldBypassFailure($method = debug_backtrace()[1]['function'])) {
return $result;
}
throw new Exception("LDAP operation [$method] failed.");
} catch (ErrorException $e) {
throw LdapRecordException::withDetailedError($e, $this->getDetailedError());
} finally {
restore_error_handler();
}
}
/**
* Determine if the failed operation should be bypassed.
*
* @param string $method
*
* @return bool
*/
protected function shouldBypassFailure($method)
{
return in_array($method, ['search', 'read', 'listing']);
}
/**
* Determine if the error should be bypassed.
*
* @param string $error
*
* @return bool
*/
protected function shouldBypassError($error)
{
return $this->causedByPaginationSupport($error) || $this->causedBySizeLimit($error) || $this->causedByNoSuchObject($error);
}
/**
* Determine if the current PHP version supports server controls.
*
* @deprecated since v2.5.0
*
* @return bool
*/
public function supportsServerControlsInMethods()
{
return version_compare(PHP_VERSION, '7.3.0') >= 0;
}
/**
* Generates an LDAP connection string for each host given.
*
* @param string|array $hosts
* @param string $port
*
* @return string
*/
protected function makeConnectionUris($hosts, $port)
{
// If an attempt to connect via SSL protocol is being performed,
// and we are still using the default port, we will swap it
// for the default SSL port, for developer convenience.
if ($this->isUsingSSL() && $port == LdapInterface::PORT) {
$port = LdapInterface::PORT_SSL;
}
// The blank space here is intentional. PHP's LDAP extension
// requires additional hosts to be seperated by a blank
// space, so that it can parse each individually.
return implode(' ', $this->assembleHostUris($hosts, $port));
}
/**
* Assemble the host URI strings.
*
* @param array|string $hosts
* @param string $port
*
* @return array
*/
protected function assembleHostUris($hosts, $port)
{
return array_map(function ($host) use ($port) {
return "{$this->getProtocol()}{$host}:{$port}";
}, (array) $hosts);
}
}