1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of phpDocumentor.
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 *
11 * @link http://phpdoc.org
12 */
13
14namespace phpDocumentor\Reflection\DocBlock\Tags;
15
16use phpDocumentor\Reflection\DocBlock\Description;
17use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
18use phpDocumentor\Reflection\Types\Context as TypeContext;
19use Webmozart\Assert\Assert;
20use function preg_match;
21
22/**
23 * Reflection class for a {@}version tag in a Docblock.
24 */
25final class Version extends BaseTag implements Factory\StaticMethod
26{
27    /** @var string */
28    protected $name = 'version';
29
30    /**
31     * PCRE regular expression matching a version vector.
32     * Assumes the "x" modifier.
33     */
34    public const REGEX_VECTOR = '(?:
35        # Normal release vectors.
36        \d\S*
37        |
38        # VCS version vectors. Per PHPCS, they are expected to
39        # follow the form of the VCS name, followed by ":", followed
40        # by the version vector itself.
41        # By convention, popular VCSes like CVS, SVN and GIT use "$"
42        # around the actual version vector.
43        [^\s\:]+\:\s*\$[^\$]+\$
44    )';
45
46    /** @var string|null The version vector. */
47    private $version;
48
49    public function __construct(?string $version = null, ?Description $description = null)
50    {
51        Assert::nullOrStringNotEmpty($version);
52
53        $this->version     = $version;
54        $this->description = $description;
55    }
56
57    public static function create(
58        ?string $body,
59        ?DescriptionFactory $descriptionFactory = null,
60        ?TypeContext $context = null
61    ) : ?self {
62        if (empty($body)) {
63            return new static();
64        }
65
66        $matches = [];
67        if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
68            return null;
69        }
70
71        $description = null;
72        if ($descriptionFactory !== null) {
73            $description = $descriptionFactory->create($matches[2] ?? '', $context);
74        }
75
76        return new static(
77            $matches[1],
78            $description
79        );
80    }
81
82    /**
83     * Gets the version section of the tag.
84     */
85    public function getVersion() : ?string
86    {
87        return $this->version;
88    }
89
90    /**
91     * Returns a string representation for this tag.
92     */
93    public function __toString() : string
94    {
95        if ($this->description) {
96            $description = $this->description->render();
97        } else {
98            $description = '';
99        }
100
101        $version = (string) $this->version;
102
103        return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : '');
104    }
105}
106