Parameter.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <?php
  2. namespace GuzzleHttp\Command\Guzzle;
  3. use GuzzleHttp\Command\ToArrayInterface;
  4. /**
  5. * API parameter object used with service descriptions
  6. */
  7. #[\AllowDynamicProperties]
  8. class Parameter implements ToArrayInterface
  9. {
  10. private $originalData;
  11. /** @var string */
  12. private $name;
  13. /** @var string */
  14. private $description;
  15. /** @var string|array */
  16. private $type;
  17. /** @var bool */
  18. private $required;
  19. /** @var array|null */
  20. private $enum;
  21. /** @var string */
  22. private $pattern;
  23. /** @var int */
  24. private $minimum;
  25. /** @var int */
  26. private $maximum;
  27. /** @var int */
  28. private $minLength;
  29. /** @var int */
  30. private $maxLength;
  31. /** @var int */
  32. private $minItems;
  33. /** @var int */
  34. private $maxItems;
  35. /** @var mixed */
  36. private $default;
  37. /** @var bool */
  38. private $static;
  39. /** @var array */
  40. private $filters;
  41. /** @var string */
  42. private $location;
  43. /** @var string */
  44. private $sentAs;
  45. /** @var array */
  46. private $data;
  47. /** @var array */
  48. private $properties = [];
  49. /** @var array|bool|Parameter */
  50. private $additionalProperties;
  51. /** @var array|Parameter */
  52. private $items;
  53. /** @var string */
  54. private $format;
  55. private $propertiesCache = null;
  56. /** @var Description */
  57. private $serviceDescription;
  58. /**
  59. * Create a new Parameter using an associative array of data.
  60. *
  61. * The array can contain the following information:
  62. *
  63. * - name: (string) Unique name of the parameter
  64. *
  65. * - type: (string|array) Type of variable (string, number, integer,
  66. * boolean, object, array, numeric, null, any). Types are used for
  67. * validation and determining the structure of a parameter. You can use a
  68. * union type by providing an array of simple types. If one of the union
  69. * types matches the provided value, then the value is valid.
  70. *
  71. * - required: (bool) Whether or not the parameter is required
  72. *
  73. * - default: (mixed) Default value to use if no value is supplied
  74. *
  75. * - static: (bool) Set to true to specify that the parameter value cannot
  76. * be changed from the default.
  77. *
  78. * - description: (string) Documentation of the parameter
  79. *
  80. * - location: (string) The location of a request used to apply a parameter.
  81. * Custom locations can be registered with a command, but the defaults
  82. * are uri, query, header, body, json, xml, formParam, multipart.
  83. *
  84. * - sentAs: (string) Specifies how the data being modeled is sent over the
  85. * wire. For example, you may wish to include certain headers in a
  86. * response model that have a normalized casing of FooBar, but the actual
  87. * header is x-foo-bar. In this case, sentAs would be set to x-foo-bar.
  88. *
  89. * - filters: (array) Array of static method names to run a parameter
  90. * value through. Each value in the array must be a string containing the
  91. * full class path to a static method or an array of complex filter
  92. * information. You can specify static methods of classes using the full
  93. * namespace class name followed by '::' (e.g. Foo\Bar::baz). Some
  94. * filters require arguments in order to properly filter a value. For
  95. * complex filters, use a hash containing a 'method' key pointing to a
  96. * static method, and an 'args' key containing an array of positional
  97. * arguments to pass to the method. Arguments can contain keywords that
  98. * are replaced when filtering a value: '@value' is replaced with the
  99. * value being validated, '@api' is replaced with the Parameter object.
  100. *
  101. * - properties: When the type is an object, you can specify nested parameters
  102. *
  103. * - additionalProperties: (array) This attribute defines a schema for all
  104. * properties that are not explicitly defined in an object type
  105. * definition. If specified, the value MUST be a schema or a boolean. If
  106. * false is provided, no additional properties are allowed beyond the
  107. * properties defined in the schema. The default value is an empty schema
  108. * which allows any value for additional properties.
  109. *
  110. * - items: This attribute defines the allowed items in an instance array,
  111. * and MUST be a schema or an array of schemas. The default value is an
  112. * empty schema which allows any value for items in the instance array.
  113. * When this attribute value is a schema and the instance value is an
  114. * array, then all the items in the array MUST be valid according to the
  115. * schema.
  116. *
  117. * - pattern: When the type is a string, you can specify the regex pattern
  118. * that a value must match
  119. *
  120. * - enum: When the type is a string, you can specify a list of acceptable
  121. * values.
  122. *
  123. * - minItems: (int) Minimum number of items allowed in an array
  124. *
  125. * - maxItems: (int) Maximum number of items allowed in an array
  126. *
  127. * - minLength: (int) Minimum length of a string
  128. *
  129. * - maxLength: (int) Maximum length of a string
  130. *
  131. * - minimum: (int) Minimum value of an integer
  132. *
  133. * - maximum: (int) Maximum value of an integer
  134. *
  135. * - data: (array) Any additional custom data to use when serializing,
  136. * validating, etc
  137. *
  138. * - format: (string) Format used to coax a value into the correct format
  139. * when serializing or unserializing. You may specify either an array of
  140. * filters OR a format, but not both. Supported values: date-time, date,
  141. * time, timestamp, date-time-http, and boolean-string.
  142. *
  143. * - $ref: (string) String referencing a service description model. The
  144. * parameter is replaced by the schema contained in the model.
  145. *
  146. * @param array $data Array of data as seen in service descriptions
  147. * @param array $options Options used when creating the parameter. You can
  148. * specify a Guzzle service description in the 'description' key.
  149. *
  150. * @throws \InvalidArgumentException
  151. */
  152. public function __construct(array $data = [], array $options = [])
  153. {
  154. $this->originalData = $data;
  155. if (isset($options['description'])) {
  156. $this->serviceDescription = $options['description'];
  157. if (!($this->serviceDescription instanceof DescriptionInterface)) {
  158. throw new \InvalidArgumentException('description must be a Description');
  159. }
  160. if (isset($data['$ref'])) {
  161. if ($model = $this->serviceDescription->getModel($data['$ref'])) {
  162. $name = isset($data['name']) ? $data['name'] : null;
  163. $data = $model->toArray() + $data;
  164. if ($name) {
  165. $data['name'] = $name;
  166. }
  167. }
  168. } elseif (isset($data['extends'])) {
  169. // If this parameter extends from another parameter then start
  170. // with the actual data union in the parent's data (e.g. actual
  171. // supersedes parent)
  172. if ($extends = $this->serviceDescription->getModel($data['extends'])) {
  173. $data += $extends->toArray();
  174. }
  175. }
  176. }
  177. // Pull configuration data into the parameter
  178. foreach ($data as $key => $value) {
  179. $this->{$key} = $value;
  180. }
  181. $this->required = (bool) $this->required;
  182. $this->data = (array) $this->data;
  183. if ($this->filters) {
  184. $this->setFilters((array) $this->filters);
  185. }
  186. if ($this->type == 'object' && $this->additionalProperties === null) {
  187. $this->additionalProperties = true;
  188. }
  189. }
  190. /**
  191. * Convert the object to an array
  192. *
  193. * @return array
  194. */
  195. public function toArray()
  196. {
  197. return $this->originalData;
  198. }
  199. /**
  200. * Get the default or static value of the command based on a value
  201. *
  202. * @param string $value Value that is currently set
  203. *
  204. * @return mixed Returns the value, a static value if one is present, or a default value
  205. */
  206. public function getValue($value)
  207. {
  208. if ($this->static || ($this->default !== null && $value === null)) {
  209. return $this->default;
  210. }
  211. return $value;
  212. }
  213. /**
  214. * Run a value through the filters OR format attribute associated with the
  215. * parameter.
  216. *
  217. * @param mixed $value Value to filter
  218. *
  219. * @return mixed Returns the filtered value
  220. *
  221. * @throws \RuntimeException when trying to format when no service
  222. * description is available.
  223. */
  224. public function filter($value)
  225. {
  226. // Formats are applied exclusively and supersed filters
  227. if ($this->format) {
  228. if (!$this->serviceDescription) {
  229. throw new \RuntimeException('No service description was set so '
  230. .'the value cannot be formatted.');
  231. }
  232. return $this->serviceDescription->format($this->format, $value);
  233. }
  234. // Convert Boolean values
  235. if ($this->type == 'boolean' && !is_bool($value)) {
  236. $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
  237. }
  238. // Apply filters to the value
  239. if ($this->filters) {
  240. foreach ($this->filters as $filter) {
  241. if (is_array($filter)) {
  242. // Convert complex filters that hold value place holders
  243. foreach ($filter['args'] as &$data) {
  244. if ($data == '@value') {
  245. $data = $value;
  246. } elseif ($data == '@api') {
  247. $data = $this;
  248. }
  249. }
  250. $value = call_user_func_array(
  251. $filter['method'],
  252. $filter['args']
  253. );
  254. } else {
  255. $value = call_user_func($filter, $value);
  256. }
  257. }
  258. }
  259. return $value;
  260. }
  261. /**
  262. * Get the name of the parameter
  263. *
  264. * @return string
  265. */
  266. public function getName()
  267. {
  268. return $this->name;
  269. }
  270. /**
  271. * Set the name of the parameter
  272. *
  273. * @param string $name Name to set
  274. */
  275. public function setName($name)
  276. {
  277. $this->name = $name;
  278. }
  279. /**
  280. * Get the key of the parameter, where sentAs will supersede name if it is
  281. * set.
  282. *
  283. * @return string
  284. */
  285. public function getWireName()
  286. {
  287. return $this->sentAs ?: $this->name;
  288. }
  289. /**
  290. * Get the type(s) of the parameter
  291. *
  292. * @return string|array
  293. */
  294. public function getType()
  295. {
  296. return $this->type;
  297. }
  298. /**
  299. * Get if the parameter is required
  300. *
  301. * @return bool
  302. */
  303. public function isRequired()
  304. {
  305. return $this->required;
  306. }
  307. /**
  308. * Get the default value of the parameter
  309. *
  310. * @return string|null
  311. */
  312. public function getDefault()
  313. {
  314. return $this->default;
  315. }
  316. /**
  317. * Get the description of the parameter
  318. *
  319. * @return string|null
  320. */
  321. public function getDescription()
  322. {
  323. return $this->description;
  324. }
  325. /**
  326. * Get the minimum acceptable value for an integer
  327. *
  328. * @return int|null
  329. */
  330. public function getMinimum()
  331. {
  332. return $this->minimum;
  333. }
  334. /**
  335. * Get the maximum acceptable value for an integer
  336. *
  337. * @return int|null
  338. */
  339. public function getMaximum()
  340. {
  341. return $this->maximum;
  342. }
  343. /**
  344. * Get the minimum allowed length of a string value
  345. *
  346. * @return int
  347. */
  348. public function getMinLength()
  349. {
  350. return $this->minLength;
  351. }
  352. /**
  353. * Get the maximum allowed length of a string value
  354. *
  355. * @return int|null
  356. */
  357. public function getMaxLength()
  358. {
  359. return $this->maxLength;
  360. }
  361. /**
  362. * Get the maximum allowed number of items in an array value
  363. *
  364. * @return int|null
  365. */
  366. public function getMaxItems()
  367. {
  368. return $this->maxItems;
  369. }
  370. /**
  371. * Get the minimum allowed number of items in an array value
  372. *
  373. * @return int
  374. */
  375. public function getMinItems()
  376. {
  377. return $this->minItems;
  378. }
  379. /**
  380. * Get the location of the parameter
  381. *
  382. * @return string|null
  383. */
  384. public function getLocation()
  385. {
  386. return $this->location;
  387. }
  388. /**
  389. * Get the sentAs attribute of the parameter that used with locations to
  390. * sentAs an attribute when it is being applied to a location.
  391. *
  392. * @return string|null
  393. */
  394. public function getSentAs()
  395. {
  396. return $this->sentAs;
  397. }
  398. /**
  399. * Retrieve a known property from the parameter by name or a data property
  400. * by name. When no specific name value is passed, all data properties
  401. * will be returned.
  402. *
  403. * @param string|null $name Specify a particular property name to retrieve
  404. *
  405. * @return array|mixed|null
  406. */
  407. public function getData($name = null)
  408. {
  409. if (!$name) {
  410. return $this->data;
  411. } elseif (isset($this->data[$name])) {
  412. return $this->data[$name];
  413. } elseif (isset($this->{$name})) {
  414. return $this->{$name};
  415. }
  416. return null;
  417. }
  418. /**
  419. * Get whether or not the default value can be changed
  420. *
  421. * @return bool
  422. */
  423. public function isStatic()
  424. {
  425. return $this->static;
  426. }
  427. /**
  428. * Get an array of filters used by the parameter
  429. *
  430. * @return array
  431. */
  432. public function getFilters()
  433. {
  434. return $this->filters ?: [];
  435. }
  436. /**
  437. * Get the properties of the parameter
  438. *
  439. * @return Parameter[]
  440. */
  441. public function getProperties()
  442. {
  443. if (!$this->propertiesCache) {
  444. $this->propertiesCache = [];
  445. foreach (array_keys($this->properties) as $name) {
  446. $this->propertiesCache[$name] = $this->getProperty($name);
  447. }
  448. }
  449. return $this->propertiesCache;
  450. }
  451. /**
  452. * Get a specific property from the parameter
  453. *
  454. * @param string $name Name of the property to retrieve
  455. *
  456. * @return Parameter|null
  457. */
  458. public function getProperty($name)
  459. {
  460. if (!isset($this->properties[$name])) {
  461. return null;
  462. }
  463. if (!($this->properties[$name] instanceof self)) {
  464. $this->properties[$name]['name'] = $name;
  465. $this->properties[$name] = new static(
  466. $this->properties[$name],
  467. ['description' => $this->serviceDescription]
  468. );
  469. }
  470. return $this->properties[$name];
  471. }
  472. /**
  473. * Get the additionalProperties value of the parameter
  474. *
  475. * @return bool|Parameter|null
  476. */
  477. public function getAdditionalProperties()
  478. {
  479. if (is_array($this->additionalProperties)) {
  480. $this->additionalProperties = new static(
  481. $this->additionalProperties,
  482. ['description' => $this->serviceDescription]
  483. );
  484. }
  485. return $this->additionalProperties;
  486. }
  487. /**
  488. * Get the item data of the parameter
  489. *
  490. * @return Parameter
  491. */
  492. public function getItems()
  493. {
  494. if (is_array($this->items)) {
  495. $this->items = new static(
  496. $this->items,
  497. ['description' => $this->serviceDescription]
  498. );
  499. }
  500. return $this->items;
  501. }
  502. /**
  503. * Get the enum of strings that are valid for the parameter
  504. *
  505. * @return array|null
  506. */
  507. public function getEnum()
  508. {
  509. return $this->enum;
  510. }
  511. /**
  512. * Get the regex pattern that must match a value when the value is a string
  513. *
  514. * @return string
  515. */
  516. public function getPattern()
  517. {
  518. return $this->pattern;
  519. }
  520. /**
  521. * Get the format attribute of the schema
  522. *
  523. * @return string
  524. */
  525. public function getFormat()
  526. {
  527. return $this->format;
  528. }
  529. /**
  530. * Set the array of filters used by the parameter
  531. *
  532. * @param array $filters Array of functions to use as filters
  533. *
  534. * @return self
  535. */
  536. private function setFilters(array $filters)
  537. {
  538. $this->filters = [];
  539. foreach ($filters as $filter) {
  540. $this->addFilter($filter);
  541. }
  542. return $this;
  543. }
  544. /**
  545. * Add a filter to the parameter
  546. *
  547. * @param string|array $filter Method to filter the value through
  548. *
  549. * @return self
  550. *
  551. * @throws \InvalidArgumentException
  552. */
  553. private function addFilter($filter)
  554. {
  555. if (is_array($filter)) {
  556. if (!isset($filter['method'])) {
  557. throw new \InvalidArgumentException(
  558. 'A [method] value must be specified for each complex filter'
  559. );
  560. }
  561. }
  562. if (!$this->filters) {
  563. $this->filters = [$filter];
  564. } else {
  565. $this->filters[] = $filter;
  566. }
  567. return $this;
  568. }
  569. /**
  570. * Check if a parameter has a specific variable and if it set.
  571. *
  572. * @param string $var
  573. *
  574. * @return bool
  575. */
  576. public function has($var)
  577. {
  578. if (!is_string($var)) {
  579. throw new \InvalidArgumentException('Expected a string. Got: '.(is_object($var) ? get_class($var) : gettype($var)));
  580. }
  581. return isset($this->{$var}) && !empty($this->{$var});
  582. }
  583. }