1<?php 2 3/** 4 * @see https://github.com/laminas/laminas-code for the canonical source repository 5 * @copyright https://github.com/laminas/laminas-code/blob/master/COPYRIGHT.md 6 * @license https://github.com/laminas/laminas-code/blob/master/LICENSE.md New BSD License 7 */ 8 9namespace Laminas\Code\Generator; 10 11use Laminas\Code\Reflection\ParameterReflection; 12use ReflectionParameter; 13 14use function is_string; 15use function method_exists; 16use function str_replace; 17use function strtolower; 18 19class ParameterGenerator extends AbstractGenerator 20{ 21 /** 22 * @var string 23 */ 24 protected $name; 25 26 /** 27 * @var TypeGenerator|null 28 */ 29 protected $type; 30 31 /** 32 * @var ValueGenerator 33 */ 34 protected $defaultValue; 35 36 /** 37 * @var int 38 */ 39 protected $position; 40 41 /** 42 * @var bool 43 */ 44 protected $passedByReference = false; 45 46 /** 47 * @var bool 48 */ 49 private $variadic = false; 50 51 /** 52 * @var bool 53 */ 54 private $omitDefaultValue = false; 55 56 /** 57 * @param ParameterReflection $reflectionParameter 58 * @return ParameterGenerator 59 */ 60 public static function fromReflection(ParameterReflection $reflectionParameter) 61 { 62 $param = new ParameterGenerator(); 63 64 $param->setName($reflectionParameter->getName()); 65 66 if ($type = self::extractFQCNTypeFromReflectionType($reflectionParameter)) { 67 $param->setType($type); 68 } 69 70 $param->setPosition($reflectionParameter->getPosition()); 71 72 $variadic = method_exists($reflectionParameter, 'isVariadic') && $reflectionParameter->isVariadic(); 73 74 $param->setVariadic($variadic); 75 76 if (! $variadic && ($reflectionParameter->isOptional() || $reflectionParameter->isDefaultValueAvailable())) { 77 try { 78 $param->setDefaultValue($reflectionParameter->getDefaultValue()); 79 } catch (\ReflectionException $e) { 80 $param->setDefaultValue(null); 81 } 82 } 83 84 $param->setPassedByReference($reflectionParameter->isPassedByReference()); 85 86 return $param; 87 } 88 89 /** 90 * Generate from array 91 * 92 * @configkey name string [required] Class Name 93 * @configkey type string 94 * @configkey defaultvalue null|bool|string|int|float|array|ValueGenerator 95 * @configkey passedbyreference bool 96 * @configkey position int 97 * @configkey sourcedirty bool 98 * @configkey indentation string 99 * @configkey sourcecontent string 100 * @configkey omitdefaultvalue bool 101 * 102 * @throws Exception\InvalidArgumentException 103 * @param array $array 104 * @return ParameterGenerator 105 */ 106 public static function fromArray(array $array) 107 { 108 if (! isset($array['name'])) { 109 throw new Exception\InvalidArgumentException( 110 'Parameter generator requires that a name is provided for this object' 111 ); 112 } 113 114 $param = new static($array['name']); 115 foreach ($array as $name => $value) { 116 // normalize key 117 switch (strtolower(str_replace(['.', '-', '_'], '', $name))) { 118 case 'type': 119 $param->setType($value); 120 break; 121 case 'defaultvalue': 122 $param->setDefaultValue($value); 123 break; 124 case 'passedbyreference': 125 $param->setPassedByReference($value); 126 break; 127 case 'position': 128 $param->setPosition($value); 129 break; 130 case 'sourcedirty': 131 $param->setSourceDirty($value); 132 break; 133 case 'indentation': 134 $param->setIndentation($value); 135 break; 136 case 'sourcecontent': 137 $param->setSourceContent($value); 138 break; 139 case 'omitdefaultvalue': 140 $param->omitDefaultValue($value); 141 break; 142 } 143 } 144 145 return $param; 146 } 147 148 /** 149 * @param string $name 150 * @param string $type 151 * @param mixed $defaultValue 152 * @param int $position 153 * @param bool $passByReference 154 */ 155 public function __construct( 156 $name = null, 157 $type = null, 158 $defaultValue = null, 159 $position = null, 160 $passByReference = false 161 ) { 162 if (null !== $name) { 163 $this->setName($name); 164 } 165 if (null !== $type) { 166 $this->setType($type); 167 } 168 if (null !== $defaultValue) { 169 $this->setDefaultValue($defaultValue); 170 } 171 if (null !== $position) { 172 $this->setPosition($position); 173 } 174 if (false !== $passByReference) { 175 $this->setPassedByReference(true); 176 } 177 } 178 179 /** 180 * @param string $type 181 * @return ParameterGenerator 182 */ 183 public function setType($type) 184 { 185 $this->type = TypeGenerator::fromTypeString($type); 186 187 return $this; 188 } 189 190 /** 191 * @return string 192 */ 193 public function getType() 194 { 195 return $this->type 196 ? (string) $this->type 197 : null; 198 } 199 200 /** 201 * @param string $name 202 * @return ParameterGenerator 203 */ 204 public function setName($name) 205 { 206 $this->name = (string) $name; 207 return $this; 208 } 209 210 /** 211 * @return string 212 */ 213 public function getName() 214 { 215 return $this->name; 216 } 217 218 /** 219 * Set the default value of the parameter. 220 * 221 * Certain variables are difficult to express 222 * 223 * @param null|bool|string|int|float|array|ValueGenerator $defaultValue 224 * @return ParameterGenerator 225 */ 226 public function setDefaultValue($defaultValue) 227 { 228 if (! $defaultValue instanceof ValueGenerator) { 229 $defaultValue = new ValueGenerator($defaultValue); 230 } 231 $this->defaultValue = $defaultValue; 232 233 return $this; 234 } 235 236 /** 237 * @return ValueGenerator 238 */ 239 public function getDefaultValue() 240 { 241 return $this->defaultValue; 242 } 243 244 /** 245 * @param int $position 246 * @return ParameterGenerator 247 */ 248 public function setPosition($position) 249 { 250 $this->position = (int) $position; 251 return $this; 252 } 253 254 /** 255 * @return int 256 */ 257 public function getPosition() 258 { 259 return $this->position; 260 } 261 262 /** 263 * @return bool 264 */ 265 public function getPassedByReference() 266 { 267 return $this->passedByReference; 268 } 269 270 /** 271 * @param bool $passedByReference 272 * @return ParameterGenerator 273 */ 274 public function setPassedByReference($passedByReference) 275 { 276 $this->passedByReference = (bool) $passedByReference; 277 return $this; 278 } 279 280 /** 281 * @param bool $variadic 282 * 283 * @return ParameterGenerator 284 */ 285 public function setVariadic($variadic) 286 { 287 $this->variadic = (bool) $variadic; 288 289 return $this; 290 } 291 292 /** 293 * @return bool 294 */ 295 public function getVariadic() 296 { 297 return $this->variadic; 298 } 299 300 /** 301 * @return string 302 */ 303 public function generate() 304 { 305 $output = $this->generateTypeHint(); 306 307 if (true === $this->passedByReference) { 308 $output .= '&'; 309 } 310 311 if ($this->variadic) { 312 $output .= '... '; 313 } 314 315 $output .= '$' . $this->name; 316 317 if ($this->omitDefaultValue) { 318 return $output; 319 } 320 321 if ($this->defaultValue instanceof ValueGenerator) { 322 $output .= ' = '; 323 $this->defaultValue->setOutputMode(ValueGenerator::OUTPUT_SINGLE_LINE); 324 $output .= $this->defaultValue; 325 } 326 327 return $output; 328 } 329 330 /** 331 * @param ParameterReflection $reflectionParameter 332 * 333 * @return null|string 334 */ 335 private static function extractFQCNTypeFromReflectionType(ParameterReflection $reflectionParameter) 336 { 337 if (! method_exists($reflectionParameter, 'getType')) { 338 return self::prePhp7ExtractFQCNTypeFromReflectionType($reflectionParameter); 339 } 340 341 $type = method_exists($reflectionParameter, 'getType') 342 ? $reflectionParameter->getType() 343 : null; 344 345 if (! $type) { 346 return null; 347 } 348 349 if (! method_exists($type, 'getName')) { 350 return self::expandLiteralParameterType((string) $type, $reflectionParameter); 351 } 352 353 return ($type->allowsNull() ? '?' : '') 354 . self::expandLiteralParameterType($type->getName(), $reflectionParameter); 355 } 356 357 /** 358 * For ancient PHP versions (yes, you should upgrade to 7.0): 359 * 360 * @param ParameterReflection $reflectionParameter 361 * 362 * @return string|null 363 */ 364 private static function prePhp7ExtractFQCNTypeFromReflectionType(ParameterReflection $reflectionParameter) 365 { 366 if ($reflectionParameter->isCallable()) { 367 return 'callable'; 368 } 369 370 if ($reflectionParameter->isArray()) { 371 return 'array'; 372 } 373 374 if ($class = $reflectionParameter->getClass()) { 375 return $class->getName(); 376 } 377 378 return null; 379 } 380 381 /** 382 * @param string $literalParameterType 383 * @param ReflectionParameter $reflectionParameter 384 * 385 * @return string 386 */ 387 private static function expandLiteralParameterType($literalParameterType, ReflectionParameter $reflectionParameter) 388 { 389 if ('self' === strtolower($literalParameterType)) { 390 return $reflectionParameter->getDeclaringClass()->getName(); 391 } 392 393 if ('parent' === strtolower($literalParameterType)) { 394 return $reflectionParameter->getDeclaringClass()->getParentClass()->getName(); 395 } 396 397 return $literalParameterType; 398 } 399 400 /** 401 * @return string 402 */ 403 private function generateTypeHint() 404 { 405 if (null === $this->type) { 406 return ''; 407 } 408 409 return $this->type->generate() . ' '; 410 } 411 412 /** 413 * @param bool $omit 414 * @return ParameterGenerator 415 */ 416 public function omitDefaultValue(bool $omit = true) 417 { 418 $this->omitDefaultValue = $omit; 419 420 return $this; 421 } 422} 423