Parameter.php 18 KB

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