234 lines
6.8 KiB
PHP
234 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace EesyLDAP\Schema;
|
|
|
|
|
|
/**
|
|
* @property-read string $oid
|
|
* @property-read string $name
|
|
* @property-read array<string> $names
|
|
* @property-read array<string,string> $property_aliases
|
|
*/
|
|
class SchemaEntry {
|
|
|
|
/**
|
|
* Default properties value
|
|
* @var array<string,mixed>
|
|
*/
|
|
protected static $default_properties = array(
|
|
'oid' => null,
|
|
'name' => array(),
|
|
);
|
|
|
|
/**
|
|
* Properties name aliases
|
|
* @var array<string,string>
|
|
*/
|
|
protected static $property_aliases = array();
|
|
|
|
/**
|
|
* Computed properties name
|
|
* @var array<string>
|
|
*/
|
|
protected static $computed_properties = array(
|
|
'names',
|
|
'property_aliases',
|
|
);
|
|
|
|
/**
|
|
* Schema entry data parsed from SubSchema attribute value
|
|
* @see parse()
|
|
* @var array<string,mixed>
|
|
*/
|
|
protected $data;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param array<string,mixed> $data Parsed schema entry data
|
|
* @return void
|
|
*/
|
|
public function __construct($data) {
|
|
$this -> data = $data;
|
|
}
|
|
|
|
/**
|
|
* Parse an SubSchema attribute value into a hash of entry properties
|
|
*
|
|
* Note: mainly from PEAR Net_LDAP2_Schema::_parse_entry()
|
|
* @link https://pear.php.net/package/Net_LDAP2
|
|
*
|
|
* @param string $value Attribute value
|
|
* @return array<string,mixed>
|
|
*/
|
|
public static function _parse($value) {
|
|
// tokens that have no value associated
|
|
$noValue = array(
|
|
'single-value',
|
|
'obsolete',
|
|
'collective',
|
|
'no-user-modification',
|
|
'abstract',
|
|
'structural',
|
|
'auxiliary'
|
|
);
|
|
|
|
// tokens that can have multiple values
|
|
$multiValue = array('name', 'must', 'may', 'sup');
|
|
|
|
// initilization
|
|
$schema_entry = array();
|
|
|
|
$tokens = self :: _tokenize($value); // get an array of tokens
|
|
|
|
// remove surrounding brackets
|
|
if ($tokens[0] == '(') array_shift($tokens);
|
|
if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-(
|
|
|
|
$schema_entry['oid'] = array_shift($tokens); // first token is the oid
|
|
|
|
// cycle over the tokens until none are left
|
|
while (count($tokens) > 0) {
|
|
$token = strtolower(array_shift($tokens));
|
|
if (in_array($token, $noValue)) {
|
|
$schema_entry[$token] = 1; // single value token
|
|
}
|
|
else {
|
|
// this one follows a string or a list if it is multivalued
|
|
if (($schema_entry[$token] = array_shift($tokens)) == '(') {
|
|
// this creates the list of values and cycles through the tokens
|
|
// until the end of the list is reached ')'
|
|
$schema_entry[$token] = array();
|
|
while ($tmp = array_shift($tokens)) {
|
|
if ($tmp == ')') break;
|
|
if ($tmp != '$') array_push($schema_entry[$token], $tmp);
|
|
}
|
|
}
|
|
// create a array if the value should be multivalued but was not
|
|
if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) {
|
|
$schema_entry[$token] = array($schema_entry[$token]);
|
|
}
|
|
}
|
|
}
|
|
// get max length from syntax
|
|
if (key_exists('syntax', $schema_entry)) {
|
|
// @phpstan-ignore-next-line
|
|
if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) {
|
|
$schema_entry['max_length'] = intval($matches[1]);
|
|
}
|
|
}
|
|
return $schema_entry;
|
|
}
|
|
|
|
/**
|
|
* Parse a SubSchema attribute value into a SchemaEntry object
|
|
* @param string $value Attribute value
|
|
* @return SchemaEntry|false
|
|
*/
|
|
public static function parse($value) {
|
|
$schema_entry = self :: _parse($value);
|
|
$type = get_called_class();
|
|
return new $type($schema_entry);
|
|
}
|
|
|
|
/**
|
|
* Tokenizes the given value into an array of tokens
|
|
*
|
|
* Note: mainly from PEAR Net_LDAP2_Schema::_tokenize()
|
|
* @link https://pear.php.net/package/Net_LDAP2
|
|
*
|
|
* @param string $value String to parse
|
|
*
|
|
* @access protected
|
|
* @return array<string> Array of tokens
|
|
*/
|
|
protected static function _tokenize($value) {
|
|
$tokens = array(); // array of tokens
|
|
$matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns
|
|
|
|
// this one is taken from perl-ldap, modified for php
|
|
$pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
|
|
|
|
/**
|
|
* This one matches one big pattern wherin only one of the three subpatterns matched
|
|
* We are interested in the subpatterns that matched. If it matched its value will be
|
|
* non-empty and so it is a token. Tokens may be round brackets, a string, or a string
|
|
* enclosed by '
|
|
*/
|
|
preg_match_all($pattern, $value, $matches);
|
|
|
|
for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match)
|
|
for ($j = 1; $j < 4; $j++) { // each subpattern
|
|
if (null != trim($matches[$j][$i])) { // pattern match in this subpattern
|
|
$tokens[$i] = trim($matches[$j][$i]); // this is the token
|
|
}
|
|
}
|
|
}
|
|
return $tokens;
|
|
}
|
|
|
|
/**
|
|
* Magic method to get schema entry key
|
|
* @param string $key
|
|
* @return mixed
|
|
* @throws \EesyLDAP\InvalidPropertyException
|
|
*/
|
|
public function __get($key) {
|
|
if (array_key_exists($key, static :: $property_aliases))
|
|
$key = static :: $property_aliases[$key];
|
|
if (
|
|
!array_key_exists($key, static :: $default_properties)
|
|
&& !in_array($key, static :: $computed_properties)
|
|
)
|
|
throw new \EesyLDAP\InvalidPropertyException(
|
|
"Invalid property '$key' requested on '".get_called_class()."'"
|
|
);
|
|
switch($key) {
|
|
case 'name':
|
|
return (
|
|
isset($this -> data['name']) && $this -> data['name']?
|
|
$this -> data['name'][0]:$this -> oid // @phpstan-ignore-line
|
|
);
|
|
case 'names':
|
|
return (
|
|
isset($this -> data['name']) && $this -> data['name']?
|
|
$this -> data['name']:array($this -> oid)
|
|
);
|
|
case 'property_aliases':
|
|
return static :: $property_aliases;
|
|
}
|
|
$default = isset(static :: $default_properties[$key])?static :: $default_properties[$key]:null;
|
|
if (!array_key_exists($key, $this -> data))
|
|
return $default;
|
|
if (is_bool($default))
|
|
return boolval($this->data[$key]);
|
|
if (is_array($default) && !is_array($this->data[$key]))
|
|
return is_null($this->data[$key])?array():array($this->data[$key]);
|
|
return $this->data[$key];
|
|
}
|
|
|
|
/**
|
|
* Magic method to compute string representation of this schema entry object
|
|
* @return string
|
|
*/
|
|
public function __toString() {
|
|
return sprintf('%s<%s>', get_called_class(), $this->name);
|
|
}
|
|
|
|
/**
|
|
* Helper to check if the provided identifier match with this schema entry
|
|
* @param string $id
|
|
* @return bool
|
|
*/
|
|
public function is_me($id) {
|
|
if (!is_string($id))
|
|
return false;
|
|
if ($id == $this->oid)
|
|
return true;
|
|
$id = strtolower($id);
|
|
foreach($this->names as $name)
|
|
if (strtolower($name) == $id)
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|