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\DocBlock;
14
15use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
16use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
17use Webmozart\Assert\Assert;
18
19/**
20 * Object representing to description for a DocBlock.
21 *
22 * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine
23 * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete
24 * description text using the format that you would prefer.
25 *
26 * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is
27 * thus recommended to use that to create a Description object, like this:
28 *
29 *     $description = $descriptionFactory->create('This is a {@see Description}', $context);
30 *
31 * The description factory will interpret the given body and create a body template and list of tags from them, and pass
32 * that onto the constructor if this class.
33 *
34 * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace
35 * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial
36 * > type names and FQSENs.
37 *
38 * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this:
39 *
40 *     $description = new Description(
41 *         'This is a %1$s',
42 *         [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ]
43 *     );
44 *
45 * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object
46 * is mainly responsible for rendering.
47 *
48 * @see DescriptionFactory to create a new Description.
49 * @see Description\Formatter for the formatting of the body and tags.
50 */
51class Description
52{
53    /** @var string */
54    private $bodyTemplate;
55
56    /** @var Tag[] */
57    private $tags;
58
59    /**
60     * Initializes a Description with its body (template) and a listing of the tags used in the body template.
61     *
62     * @param string $bodyTemplate
63     * @param Tag[] $tags
64     */
65    public function __construct($bodyTemplate, array $tags = [])
66    {
67        Assert::string($bodyTemplate);
68
69        $this->bodyTemplate = $bodyTemplate;
70        $this->tags = $tags;
71    }
72
73    /**
74     * Returns the tags for this DocBlock.
75     *
76     * @return Tag[]
77     */
78    public function getTags()
79    {
80        return $this->tags;
81    }
82
83    /**
84     * Renders this description as a string where the provided formatter will format the tags in the expected string
85     * format.
86     *
87     * @param Formatter|null $formatter
88     *
89     * @return string
90     */
91    public function render(Formatter $formatter = null)
92    {
93        if ($formatter === null) {
94            $formatter = new PassthroughFormatter();
95        }
96
97        $tags = [];
98        foreach ($this->tags as $tag) {
99            $tags[] = '{' . $formatter->format($tag) . '}';
100        }
101
102        return vsprintf($this->bodyTemplate, $tags);
103    }
104
105    /**
106     * Returns a plain string representation of this description.
107     *
108     * @return string
109     */
110    public function __toString()
111    {
112        return $this->render();
113    }
114}
115