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\Tags;
14
15use phpDocumentor\Reflection\DocBlock\Description;
16use phpDocumentor\Reflection\DocBlock\Tag;
17use Webmozart\Assert\Assert;
18
19/**
20 * Reflection class for a {@}example tag in a Docblock.
21 */
22final class Example extends BaseTag
23{
24    /**
25     * @var string Path to a file to use as an example. May also be an absolute URI.
26     */
27    private $filePath;
28
29    /**
30     * @var bool Whether the file path component represents an URI. This determines how the file portion
31     *     appears at {@link getContent()}.
32     */
33    private $isURI = false;
34
35    /**
36     * @var int
37     */
38    private $startingLine;
39
40    /**
41     * @var int
42     */
43    private $lineCount;
44
45    public function __construct($filePath, $isURI, $startingLine, $lineCount, $description)
46    {
47        Assert::notEmpty($filePath);
48        Assert::integer($startingLine);
49        Assert::greaterThanEq($startingLine, 0);
50
51        $this->filePath = $filePath;
52        $this->startingLine = $startingLine;
53        $this->lineCount = $lineCount;
54        $this->name = 'example';
55        if ($description !== null) {
56            $this->description = trim($description);
57        }
58
59        $this->isURI = $isURI;
60    }
61
62    /**
63     * {@inheritdoc}
64     */
65    public function getContent()
66    {
67        if (null === $this->description) {
68            $filePath = '"' . $this->filePath . '"';
69            if ($this->isURI) {
70                $filePath = $this->isUriRelative($this->filePath)
71                    ? str_replace('%2F', '/', rawurlencode($this->filePath))
72                    :$this->filePath;
73            }
74
75            return trim($filePath . ' ' . parent::getDescription());
76        }
77
78        return $this->description;
79    }
80
81    /**
82     * {@inheritdoc}
83     */
84    public static function create($body)
85    {
86        // File component: File path in quotes or File URI / Source information
87        if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
88            return null;
89        }
90
91        $filePath = null;
92        $fileUri  = null;
93        if ('' !== $matches[1]) {
94            $filePath = $matches[1];
95        } else {
96            $fileUri = $matches[2];
97        }
98
99        $startingLine = 1;
100        $lineCount    = null;
101        $description  = null;
102
103        if (array_key_exists(3, $matches)) {
104            $description = $matches[3];
105
106            // Starting line / Number of lines / Description
107            if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) {
108                $startingLine = (int)$contentMatches[1];
109                if (isset($contentMatches[2]) && $contentMatches[2] !== '') {
110                    $lineCount = (int)$contentMatches[2];
111                }
112
113                if (array_key_exists(3, $contentMatches)) {
114                    $description = $contentMatches[3];
115                }
116            }
117        }
118
119        return new static(
120            $filePath !== null?$filePath:$fileUri,
121            $fileUri !== null,
122            $startingLine,
123            $lineCount,
124            $description
125        );
126    }
127
128    /**
129     * Returns the file path.
130     *
131     * @return string Path to a file to use as an example.
132     *     May also be an absolute URI.
133     */
134    public function getFilePath()
135    {
136        return $this->filePath;
137    }
138
139    /**
140     * Returns a string representation for this tag.
141     *
142     * @return string
143     */
144    public function __toString()
145    {
146        return $this->filePath . ($this->description ? ' ' . $this->description : '');
147    }
148
149    /**
150     * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute).
151     *
152     * @param string $uri
153     *
154     * @return bool
155     */
156    private function isUriRelative($uri)
157    {
158        return false === strpos($uri, ':');
159    }
160
161    /**
162     * @return int
163     */
164    public function getStartingLine()
165    {
166        return $this->startingLine;
167    }
168
169    /**
170     * @return int
171     */
172    public function getLineCount()
173    {
174        return $this->lineCount;
175    }
176}
177