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\DescriptionFactory;
17use phpDocumentor\Reflection\Types\Context as TypeContext;
18use Webmozart\Assert\Assert;
19
20/**
21 * Reflection class for a {@}source tag in a Docblock.
22 */
23final class Source extends BaseTag implements Factory\StaticMethod
24{
25    /** @var string */
26    protected $name = 'source';
27
28    /** @var int The starting line, relative to the structural element's location. */
29    private $startingLine = 1;
30
31    /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */
32    private $lineCount = null;
33
34    public function __construct($startingLine, $lineCount = null, Description $description = null)
35    {
36        Assert::integerish($startingLine);
37        Assert::nullOrIntegerish($lineCount);
38
39        $this->startingLine = (int)$startingLine;
40        $this->lineCount    = $lineCount !== null ? (int)$lineCount : null;
41        $this->description  = $description;
42    }
43
44    /**
45     * {@inheritdoc}
46     */
47    public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
48    {
49        Assert::stringNotEmpty($body);
50        Assert::notNull($descriptionFactory);
51
52        $startingLine = 1;
53        $lineCount    = null;
54        $description  = null;
55
56        // Starting line / Number of lines / Description
57        if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) {
58            $startingLine = (int)$matches[1];
59            if (isset($matches[2]) && $matches[2] !== '') {
60                $lineCount = (int)$matches[2];
61            }
62
63            $description = $matches[3];
64        }
65
66        return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context));
67    }
68
69    /**
70     * Gets the starting line.
71     *
72     * @return int The starting line, relative to the structural element's
73     *     location.
74     */
75    public function getStartingLine()
76    {
77        return $this->startingLine;
78    }
79
80    /**
81     * Returns the number of lines.
82     *
83     * @return int|null The number of lines, relative to the starting line. NULL
84     *     means "to the end".
85     */
86    public function getLineCount()
87    {
88        return $this->lineCount;
89    }
90
91    public function __toString()
92    {
93        return $this->startingLine
94        . ($this->lineCount !== null ? ' ' . $this->lineCount : '')
95        . ($this->description ? ' ' . $this->description->render() : '');
96    }
97}
98