1<?php 2/** 3 * This file is part of the FreeDSx LDAP package. 4 * 5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11namespace FreeDSx\Ldap\Control\Sorting; 12 13use FreeDSx\Asn1\Asn1; 14use FreeDSx\Asn1\Type\AbstractType; 15use FreeDSx\Asn1\Type\IncompleteType; 16use FreeDSx\Asn1\Type\OctetStringType; 17use FreeDSx\Asn1\Type\SequenceType; 18use FreeDSx\Ldap\Control\Control; 19use FreeDSx\Ldap\Exception\ProtocolException; 20use FreeDSx\Ldap\Protocol\LdapEncoder; 21 22/** 23 * A Server Side Sorting request control value. RFC 2891. 24 * 25 * SortKeyList ::= SEQUENCE OF SEQUENCE { 26 * attributeType AttributeDescription, 27 * orderingRule [0] MatchingRuleId OPTIONAL, 28 * reverseOrder [1] BOOLEAN DEFAULT FALSE } 29 * 30 * @author Chad Sikorra <Chad.Sikorra@gmail.com> 31 */ 32class SortingControl extends Control 33{ 34 /** 35 * @var SortKey[] 36 */ 37 protected $sortKeys = []; 38 39 /** 40 * @param SortKey ...$sortKeys 41 */ 42 public function __construct(SortKey ...$sortKeys) 43 { 44 $this->sortKeys = $sortKeys; 45 parent::__construct(self::OID_SORTING); 46 } 47 48 /** 49 * @param SortKey ...$sortKeys 50 * @return $this 51 */ 52 public function addSortKeys(SortKey ...$sortKeys) 53 { 54 foreach ($sortKeys as $sortKey) { 55 $this->sortKeys[] = $sortKey; 56 } 57 58 return $this; 59 } 60 61 /** 62 * @param SortKey ...$sortKeys 63 * @return $this 64 */ 65 public function setSortKeys(SortKey ...$sortKeys) 66 { 67 $this->sortKeys = $sortKeys; 68 69 return $this; 70 } 71 72 /** 73 * @return SortKey[] 74 */ 75 public function getSortKeys(): array 76 { 77 return $this->sortKeys; 78 } 79 80 /** 81 * {@inheritdoc} 82 */ 83 public static function fromAsn1(AbstractType $type) 84 { 85 $response = self::decodeEncodedValue($type); 86 if (!$response instanceof SequenceType) { 87 throw new ProtocolException('The sorting control is malformed.'); 88 } 89 90 $sortKeys = []; 91 /** @var SequenceType $response */ 92 foreach ($response->getChildren() as $sequence) { 93 if (!$sequence instanceof SequenceType) { 94 throw new ProtocolException('The sort key is malformed.'); 95 } 96 97 $attrName = ''; 98 $matchRule = null; 99 $useReverseOrder = false; 100 101 $encoder = new LdapEncoder(); 102 /** @var AbstractType $keyItem */ 103 foreach ($sequence->getChildren() as $keyItem) { 104 if ($keyItem instanceof OctetStringType && $keyItem->getTagClass() === AbstractType::TAG_CLASS_UNIVERSAL) { 105 $attrName = $keyItem->getValue(); 106 } elseif ($keyItem->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $keyItem->getTagNumber() === 0) { 107 $matchRule = $keyItem->getValue(); 108 } elseif ($keyItem->getTagClass() === AbstractType::TAG_CLASS_CONTEXT_SPECIFIC && $keyItem->getTagNumber() === 1) { 109 if (!$keyItem instanceof IncompleteType) { 110 throw new ProtocolException('The sorting control is malformed.'); 111 } 112 /** @var IncompleteType $keyItem */ 113 $useReverseOrder = $encoder->complete($keyItem, AbstractType::TAG_TYPE_BOOLEAN)->getValue(); 114 } else { 115 throw new ProtocolException('The sorting control contains unexpected data.'); 116 } 117 } 118 if ($attrName === '') { 119 throw new ProtocolException('The sort key is missing an attribute description.'); 120 } 121 122 $sortKeys[] = new SortKey($attrName, $useReverseOrder, $matchRule); 123 } 124 $control = new self(...$sortKeys); 125 126 return self::mergeControlData($control, $type); 127 } 128 129 /** 130 * {@inheritdoc} 131 */ 132 public function toAsn1(): AbstractType 133 { 134 $this->controlValue = Asn1::sequenceOf(); 135 136 foreach ($this->sortKeys as $sortKey) { 137 $child = Asn1::sequence(Asn1::octetString($sortKey->getAttribute())); 138 if ($sortKey->getOrderingRule() !== null) { 139 $child->addChild(Asn1::context(0, Asn1::octetString($sortKey->getOrderingRule()))); 140 } 141 if ($sortKey->getUseReverseOrder()) { 142 $child->addChild(Asn1::context(1, Asn1::boolean(true))); 143 } 144 $this->controlValue->addChild($child); 145 } 146 147 return parent::toAsn1(); 148 } 149} 150