php-eesyldap/src/Schema/SchemaEntry.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;
}
}