1<?php
2/**
3 * This file is part of phpDocumentor.
4 *
5 * For the full copyright and license information, please view the LICENSE
6 * file that was distributed with this source code.
7 *
8 * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
9 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10 * @link      http://phpdoc.org
11 */
12
13namespace phpDocumentor\Reflection;
14
15use phpDocumentor\Reflection\DocBlock\Tag;
16use Webmozart\Assert\Assert;
17
18final class DocBlock
19{
20    /** @var string The opening line for this docblock. */
21    private $summary = '';
22
23    /** @var DocBlock\Description The actual description for this docblock. */
24    private $description = null;
25
26    /** @var Tag[] An array containing all the tags in this docblock; except inline. */
27    private $tags = [];
28
29    /** @var Types\Context Information about the context of this DocBlock. */
30    private $context = null;
31
32    /** @var Location Information about the location of this DocBlock. */
33    private $location = null;
34
35    /** @var bool Is this DocBlock (the start of) a template? */
36    private $isTemplateStart = false;
37
38    /** @var bool Does this DocBlock signify the end of a DocBlock template? */
39    private $isTemplateEnd = false;
40
41    /**
42     * @param string $summary
43     * @param DocBlock\Description $description
44     * @param DocBlock\Tag[] $tags
45     * @param Types\Context $context The context in which the DocBlock occurs.
46     * @param Location $location The location within the file that this DocBlock occurs in.
47     * @param bool $isTemplateStart
48     * @param bool $isTemplateEnd
49     */
50    public function __construct(
51        $summary = '',
52        DocBlock\Description $description = null,
53        array $tags = [],
54        Types\Context $context = null,
55        Location $location = null,
56        $isTemplateStart = false,
57        $isTemplateEnd = false
58    ) {
59        Assert::string($summary);
60        Assert::boolean($isTemplateStart);
61        Assert::boolean($isTemplateEnd);
62        Assert::allIsInstanceOf($tags, Tag::class);
63
64        $this->summary = $summary;
65        $this->description = $description ?: new DocBlock\Description('');
66        foreach ($tags as $tag) {
67            $this->addTag($tag);
68        }
69
70        $this->context = $context;
71        $this->location = $location;
72
73        $this->isTemplateEnd = $isTemplateEnd;
74        $this->isTemplateStart = $isTemplateStart;
75    }
76
77    /**
78     * @return string
79     */
80    public function getSummary()
81    {
82        return $this->summary;
83    }
84
85    /**
86     * @return DocBlock\Description
87     */
88    public function getDescription()
89    {
90        return $this->description;
91    }
92
93    /**
94     * Returns the current context.
95     *
96     * @return Types\Context
97     */
98    public function getContext()
99    {
100        return $this->context;
101    }
102
103    /**
104     * Returns the current location.
105     *
106     * @return Location
107     */
108    public function getLocation()
109    {
110        return $this->location;
111    }
112
113    /**
114     * Returns whether this DocBlock is the start of a Template section.
115     *
116     * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
117     * (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
118     *
119     * An example of such an opening is:
120     *
121     * ```
122     * /**#@+
123     *  * My DocBlock
124     *  * /
125     * ```
126     *
127     * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
128     * elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
129     *
130     * @see self::isTemplateEnd() for the check whether a closing marker was provided.
131     *
132     * @return boolean
133     */
134    public function isTemplateStart()
135    {
136        return $this->isTemplateStart;
137    }
138
139    /**
140     * Returns whether this DocBlock is the end of a Template section.
141     *
142     * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
143     *
144     * @return boolean
145     */
146    public function isTemplateEnd()
147    {
148        return $this->isTemplateEnd;
149    }
150
151    /**
152     * Returns the tags for this DocBlock.
153     *
154     * @return Tag[]
155     */
156    public function getTags()
157    {
158        return $this->tags;
159    }
160
161    /**
162     * Returns an array of tags matching the given name. If no tags are found
163     * an empty array is returned.
164     *
165     * @param string $name String to search by.
166     *
167     * @return Tag[]
168     */
169    public function getTagsByName($name)
170    {
171        Assert::string($name);
172
173        $result = [];
174
175        /** @var Tag $tag */
176        foreach ($this->getTags() as $tag) {
177            if ($tag->getName() !== $name) {
178                continue;
179            }
180
181            $result[] = $tag;
182        }
183
184        return $result;
185    }
186
187    /**
188     * Checks if a tag of a certain type is present in this DocBlock.
189     *
190     * @param string $name Tag name to check for.
191     *
192     * @return bool
193     */
194    public function hasTag($name)
195    {
196        Assert::string($name);
197
198        /** @var Tag $tag */
199        foreach ($this->getTags() as $tag) {
200            if ($tag->getName() === $name) {
201                return true;
202            }
203        }
204
205        return false;
206    }
207
208    /**
209     * Remove a tag from this DocBlock.
210     *
211     * @param Tag $tag The tag to remove.
212     *
213     * @return void
214     */
215    public function removeTag(Tag $tagToRemove)
216    {
217        foreach ($this->tags as $key => $tag) {
218            if ($tag === $tagToRemove) {
219                unset($this->tags[$key]);
220                break;
221            }
222        }
223    }
224
225    /**
226     * Adds a tag to this DocBlock.
227     *
228     * @param Tag $tag The tag to add.
229     *
230     * @return void
231     */
232    private function addTag(Tag $tag)
233    {
234        $this->tags[] = $tag;
235    }
236}
237