1<?php
2
3/**
4 * This file is part of the FreeDSx LDAP package.
5 *
6 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace FreeDSx\Ldap\Operation\Response;
13
14use FreeDSx\Asn1\Asn1;
15use FreeDSx\Asn1\Type\AbstractType;
16use FreeDSx\Asn1\Type\OctetStringType;
17use FreeDSx\Asn1\Type\SequenceType;
18use FreeDSx\Ldap\Entry\Attribute;
19use FreeDSx\Ldap\Entry\Dn;
20use FreeDSx\Ldap\Entry\Entry;
21use FreeDSx\Ldap\Exception\ProtocolException;
22
23/**
24 * A search result entry. RFC 4511, 4.5.2.
25 *
26 * SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
27 *     objectName      LDAPDN,
28 *     attributes      PartialAttributeList }
29 *
30 * PartialAttributeList ::= SEQUENCE OF
31 *     partialAttribute PartialAttribute
32 *
33 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
34 */
35class SearchResultEntry implements ResponseInterface
36{
37    protected const TAG_NUMBER = 4;
38
39    /**
40     * @var Entry
41     */
42    protected $entry;
43
44    /**
45     * @param Entry $entry
46     */
47    public function __construct(Entry $entry)
48    {
49        $this->entry = $entry;
50    }
51
52    /**
53     * @return Entry
54     */
55    public function getEntry(): Entry
56    {
57        return $this->entry;
58    }
59
60    /**
61     * {@inheritDoc}
62     * @return self
63     */
64    public static function fromAsn1(AbstractType $type)
65    {
66        $attributes = [];
67        $dn = $type->getChild(0);
68        if ($dn === null) {
69            throw new ProtocolException('The search result entry is malformed.');
70        }
71
72        $partialAttributes = $type->getChild(1);
73        if ($partialAttributes !== null) {
74            foreach ($partialAttributes as $partialAttribute) {
75                $values = [];
76                /** @var SequenceType|null $attrValues */
77                $attrValues = $partialAttribute->getChild(1);
78                if ($attrValues !== null) {
79                    foreach ($attrValues->getChildren() as $attrValue) {
80                        /** @var OctetStringType $attrValue */
81                        $values[] = $attrValue->getValue();
82                    }
83                }
84
85                $attributes[] = new Attribute($partialAttribute->getChild(0)->getValue(), ...$values);
86            }
87        }
88
89        return new self(new Entry(
90            new Dn($dn->getValue()),
91            ...$attributes
92        ));
93    }
94
95    /**
96     * @return SequenceType
97     */
98    public function toAsn1(): AbstractType
99    {
100        /** @var SequenceType $asn1 */
101        $asn1 = Asn1::application(self::TAG_NUMBER, Asn1::sequence());
102
103        $partialAttributes = Asn1::sequenceOf();
104        foreach ($this->entry->getAttributes() as $attribute) {
105            $partialAttributes->addChild(Asn1::sequence(
106                Asn1::octetString($attribute->getDescription()),
107                Asn1::setOf(...array_map(function ($v) {
108                    return Asn1::octetString($v);
109                }, $attribute->getValues()))
110            ));
111        }
112        $asn1->addChild(Asn1::octetString($this->entry->getDn()->toString()));
113        $asn1->addChild($partialAttributes);
114
115        return $asn1;
116    }
117}
118