blob: c092173e9b4761321e35a9027733c0366e94bfd0 [file] [log] [blame]
<?php
namespace LdapRecord\Models\Attributes;
use LdapRecord\EscapesValues;
use LdapRecord\Support\Arr;
class DistinguishedName
{
use EscapesValues;
/**
* The underlying raw value.
*
* @var string|null
*/
protected $value;
/**
* Constructor.
*
* @param string|null $value
*/
public function __construct($value = null)
{
$this->value = trim($value);
}
/**
* Get the distinguished name value.
*
* @return string
*/
public function __toString()
{
return (string) $this->value;
}
/**
* Alias of the "build" method.
*
* @param string|null $value
*
* @return DistinguishedNameBuilder
*/
public static function of($value = null)
{
return static::build($value);
}
/**
* Get a new DN builder object from the given DN.
*
* @param string|null $value
*
* @return DistinguishedNameBuilder
*/
public static function build($value = null)
{
return new DistinguishedNameBuilder($value);
}
/**
* Make a new distinguished name instance.
*
* @param string|null $value
*
* @return static
*/
public static function make($value = null)
{
return new static($value);
}
/**
* Explode a distinguished name into relative distinguished names.
*
* @param string $dn
*
* @return array
*/
public static function explode($dn)
{
$dn = ldap_explode_dn($dn, $withoutAttributes = false);
if (! is_array($dn)) {
return [];
}
if (! array_key_exists('count', $dn)) {
return [];
}
unset($dn['count']);
return $dn;
}
/**
* Un-escapes a hexadecimal string into its original string representation.
*
* @param string $value
*
* @return string
*/
public static function unescape($value)
{
return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/', function ($matches) {
return chr(hexdec($matches[1]));
}, $value);
}
/**
* Explode the RDN into an attribute and value.
*
* @param string $rdn
*
* @return array
*/
public static function explodeRdn($rdn)
{
return explode('=', $rdn, $limit = 2);
}
/**
* Implode the component attribute and value into an RDN.
*
* @param string $rdn
*
* @return string
*/
public static function makeRdn(array $component)
{
return implode('=', $component);
}
/**
* Get the underlying value.
*
* @return string|null
*/
public function get()
{
return $this->value;
}
/**
* Set the underlying value.
*
* @param string|null $value
*
* @return $this
*/
public function set($value)
{
$this->value = $value;
return $this;
}
/**
* Get the distinguished name values without attributes.
*
* @return array
*/
public function values()
{
$values = [];
foreach ($this->multi() as [, $value]) {
$values[] = static::unescape($value);
}
return $values;
}
/**
* Get the distinguished name attributes without values.
*
* @return array
*/
public function attributes()
{
$attributes = [];
foreach ($this->multi() as [$attribute]) {
$attributes[] = $attribute;
}
return $attributes;
}
/**
* Get the distinguished name components with attributes.
*
* @return array
*/
public function components()
{
$components = [];
foreach ($this->multi() as [$attribute, $value]) {
// When a distinguished name is exploded, the values are automatically
// escaped. This cannot be opted out of. Here we will unescape
// the attribute value, then re-escape it to its original
// representation from the server using the "dn" flag.
$value = $this->escape(static::unescape($value))->dn();
$components[] = static::makeRdn([$attribute, $value]);
}
return $components;
}
/**
* Convert the distinguished name into an associative array.
*
* @return array
*/
public function assoc()
{
$map = [];
foreach ($this->multi() as [$attribute, $value]) {
$attribute = $this->normalize($attribute);
array_key_exists($attribute, $map)
? $map[$attribute][] = $value
: $map[$attribute] = [$value];
}
return $map;
}
/**
* Split the RDNs into a multi-dimensional array.
*
* @return array
*/
public function multi()
{
return array_map(function ($rdn) {
return static::explodeRdn($rdn);
}, $this->rdns());
}
/**
* Split the distinguished name into an array of unescaped RDN's.
*
* @return array
*/
public function rdns()
{
return static::explode($this->value);
}
/**
* Get the first RDNs value.
*
* @return string|null
*/
public function name()
{
return Arr::first($this->values());
}
/**
* Get the first RDNs attribute.
*
* @return string|null
*/
public function head()
{
return Arr::first($this->attributes());
}
/**
* Get the relative distinguished name.
*
* @return string|null
*/
public function relative()
{
return Arr::first($this->components());
}
/**
* Alias of relative().
*
* Get the first RDN from the distinguished name.
*
* @return string|null
*/
public function first()
{
return $this->relative();
}
/**
* Get the parent distinguished name.
*
* @return string|null
*/
public function parent()
{
$components = $this->components();
array_shift($components);
return implode(',', $components) ?: null;
}
/**
* Determine if the current distinguished name is a parent of the given child.
*
* @param DistinguishedName $child
*
* @return bool
*/
public function isParentOf(self $child)
{
return $child->isChildOf($this);
}
/**
* Determine if the current distinguished name is a child of the given parent.
*
* @param DistinguishedName $parent
*
* @return bool
*/
public function isChildOf(self $parent)
{
if (
empty($components = $this->components()) ||
empty($parentComponents = $parent->components())
) {
return false;
}
array_shift($components);
return $this->compare($components, $parentComponents);
}
/**
* Determine if the current distinguished name is an ancestor of the descendant.
*
* @param DistinguishedName $descendant
*
* @return bool
*/
public function isAncestorOf(self $descendant)
{
return $descendant->isDescendantOf($this);
}
/**
* Determine if the current distinguished name is a descendant of the ancestor.
*
* @param DistinguishedName $ancestor
*
* @return bool
*/
public function isDescendantOf(self $ancestor)
{
if (
empty($components = $this->components()) ||
empty($ancestorComponents = $ancestor->components())
) {
return false;
}
if (! $length = count($components) - count($ancestorComponents)) {
return false;
}
array_splice($components, $offset = 0, $length);
return $this->compare($components, $ancestorComponents);
}
/**
* Compare whether the two distinguished name values are equal.
*
* @param array $values
* @param array $other
*
* @return bool
*/
protected function compare(array $values, array $other)
{
return $this->recase($values) == $this->recase($other);
}
/**
* Recase the array values.
*
* @param array $values
*
* @return array
*/
protected function recase(array $values)
{
return array_map([$this, 'normalize'], $values);
}
/**
* Normalize the string value.
*
* @param string $value
*
* @return string
*/
protected function normalize($value)
{
return strtolower($value);
}
}