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