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\Type; 19use phpDocumentor\Reflection\TypeResolver; 20use phpDocumentor\Reflection\Types\Context as TypeContext; 21use phpDocumentor\Reflection\Utils; 22use Webmozart\Assert\Assert; 23use function array_shift; 24use function array_unshift; 25use function implode; 26use function strpos; 27use function substr; 28use const PREG_SPLIT_DELIM_CAPTURE; 29 30/** 31 * Reflection class for the {@}param tag in a Docblock. 32 */ 33final class Param extends TagWithType implements Factory\StaticMethod 34{ 35 /** @var string|null */ 36 private $variableName; 37 38 /** @var bool determines whether this is a variadic argument */ 39 private $isVariadic; 40 41 /** @var bool determines whether this is passed by reference */ 42 private $isReference; 43 44 public function __construct( 45 ?string $variableName, 46 ?Type $type = null, 47 bool $isVariadic = false, 48 ?Description $description = null, 49 bool $isReference = false 50 ) { 51 $this->name = 'param'; 52 $this->variableName = $variableName; 53 $this->type = $type; 54 $this->isVariadic = $isVariadic; 55 $this->description = $description; 56 $this->isReference = $isReference; 57 } 58 59 public static function create( 60 string $body, 61 ?TypeResolver $typeResolver = null, 62 ?DescriptionFactory $descriptionFactory = null, 63 ?TypeContext $context = null 64 ) : self { 65 Assert::stringNotEmpty($body); 66 Assert::notNull($typeResolver); 67 Assert::notNull($descriptionFactory); 68 69 [$firstPart, $body] = self::extractTypeFromBody($body); 70 71 $type = null; 72 $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); 73 $variableName = ''; 74 $isVariadic = false; 75 $isReference = false; 76 77 // if the first item that is encountered is not a variable; it is a type 78 if ($firstPart && !self::strStartsWithVariable($firstPart)) { 79 $type = $typeResolver->resolve($firstPart, $context); 80 } else { 81 // first part is not a type; we should prepend it to the parts array for further processing 82 array_unshift($parts, $firstPart); 83 } 84 85 // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name 86 if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) { 87 $variableName = array_shift($parts); 88 if ($type) { 89 array_shift($parts); 90 } 91 92 Assert::notNull($variableName); 93 94 if (strpos($variableName, '$') === 0) { 95 $variableName = substr($variableName, 1); 96 } elseif (strpos($variableName, '&$') === 0) { 97 $isReference = true; 98 $variableName = substr($variableName, 2); 99 } elseif (strpos($variableName, '...$') === 0) { 100 $isVariadic = true; 101 $variableName = substr($variableName, 4); 102 } elseif (strpos($variableName, '&...$') === 0) { 103 $isVariadic = true; 104 $isReference = true; 105 $variableName = substr($variableName, 5); 106 } 107 } 108 109 $description = $descriptionFactory->create(implode('', $parts), $context); 110 111 return new static($variableName, $type, $isVariadic, $description, $isReference); 112 } 113 114 /** 115 * Returns the variable's name. 116 */ 117 public function getVariableName() : ?string 118 { 119 return $this->variableName; 120 } 121 122 /** 123 * Returns whether this tag is variadic. 124 */ 125 public function isVariadic() : bool 126 { 127 return $this->isVariadic; 128 } 129 130 /** 131 * Returns whether this tag is passed by reference. 132 */ 133 public function isReference() : bool 134 { 135 return $this->isReference; 136 } 137 138 /** 139 * Returns a string representation for this tag. 140 */ 141 public function __toString() : string 142 { 143 if ($this->description) { 144 $description = $this->description->render(); 145 } else { 146 $description = ''; 147 } 148 149 $variableName = ''; 150 if ($this->variableName) { 151 $variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : ''); 152 $variableName .= '$' . $this->variableName; 153 } 154 155 $type = (string) $this->type; 156 157 return $type 158 . ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '') 159 . ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : ''); 160 } 161 162 private static function strStartsWithVariable(string $str) : bool 163 { 164 return strpos($str, '$') === 0 165 || 166 strpos($str, '...$') === 0 167 || 168 strpos($str, '&$') === 0 169 || 170 strpos($str, '&...$') === 0; 171 } 172} 173