1<?php
2
3/*
4 * This file is part of the league/commonmark package.
5 *
6 * (c) Colin O'Dell <colinodell@gmail.com>
7 *
8 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
9 *  - (c) John MacFarlane
10 *
11 * For the full copyright and license information, please view the LICENSE
12 * file that was distributed with this source code.
13 */
14
15namespace League\CommonMark;
16
17use League\CommonMark\Util\Xml;
18
19class HtmlElement
20{
21    /**
22     * @var string
23     */
24    protected $tagName;
25
26    /**
27     * @var string[]
28     */
29    protected $attributes = [];
30
31    /**
32     * @var HtmlElement|HtmlElement[]|string
33     */
34    protected $contents;
35
36    /**
37     * @var bool
38     */
39    protected $selfClosing = false;
40
41    /**
42     * @param string                                $tagName     Name of the HTML tag
43     * @param string[]                              $attributes  Array of attributes (values should be unescaped)
44     * @param HtmlElement|HtmlElement[]|string|null $contents    Inner contents, pre-escaped if needed
45     * @param bool                                  $selfClosing Whether the tag is self-closing
46     */
47    public function __construct(string $tagName, array $attributes = [], $contents = '', bool $selfClosing = false)
48    {
49        $this->tagName = $tagName;
50        $this->attributes = $attributes;
51        $this->selfClosing = $selfClosing;
52
53        $this->setContents($contents ?? '');
54    }
55
56    public function getTagName(): string
57    {
58        return $this->tagName;
59    }
60
61    /**
62     * @return string[]
63     */
64    public function getAllAttributes(): array
65    {
66        return $this->attributes;
67    }
68
69    public function getAttribute(string $key): ?string
70    {
71        if (!isset($this->attributes[$key])) {
72            return null;
73        }
74
75        return $this->attributes[$key];
76    }
77
78    public function setAttribute(string $key, string $value): self
79    {
80        $this->attributes[$key] = $value;
81
82        return $this;
83    }
84
85    /**
86     * @param bool $asString
87     *
88     * @return HtmlElement|HtmlElement[]|string
89     */
90    public function getContents(bool $asString = true)
91    {
92        if (!$asString) {
93            return $this->contents;
94        }
95
96        return $this->getContentsAsString();
97    }
98
99    /**
100     * Sets the inner contents of the tag (must be pre-escaped if needed)
101     *
102     * @param HtmlElement|HtmlElement[]|string $contents
103     *
104     * @return $this
105     */
106    public function setContents($contents): self
107    {
108        $this->contents = $contents ?? '';
109
110        return $this;
111    }
112
113    public function __toString(): string
114    {
115        $result = '<' . $this->tagName;
116
117        foreach ($this->attributes as $key => $value) {
118            $result .= ' ' . $key . '="' . Xml::escape($value) . '"';
119        }
120
121        if ($this->contents !== '') {
122            $result .= '>' . $this->getContentsAsString() . '</' . $this->tagName . '>';
123        } elseif ($this->selfClosing && $this->tagName === 'input') {
124            $result .= '>';
125        } elseif ($this->selfClosing) {
126            $result .= ' />';
127        } else {
128            $result .= '></' . $this->tagName . '>';
129        }
130
131        return $result;
132    }
133
134    private function getContentsAsString(): string
135    {
136        if (\is_string($this->contents)) {
137            return $this->contents;
138        }
139
140        if (\is_array($this->contents)) {
141            return \implode('', $this->contents);
142        }
143
144        return (string) $this->contents;
145    }
146}
147