Parameter.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <?php
  2. namespace Sabre\VObject;
  3. use ArrayIterator;
  4. use Sabre\Xml;
  5. /**
  6. * VObject Parameter.
  7. *
  8. * This class represents a parameter. A parameter is always tied to a property.
  9. * In the case of:
  10. * DTSTART;VALUE=DATE:20101108
  11. * VALUE=DATE would be the parameter name and value.
  12. *
  13. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  14. * @author Evert Pot (http://evertpot.com/)
  15. * @license http://sabre.io/license/ Modified BSD License
  16. */
  17. class Parameter extends Node
  18. {
  19. /**
  20. * Parameter name.
  21. *
  22. * @var string
  23. */
  24. public $name;
  25. /**
  26. * vCard 2.1 allows parameters to be encoded without a name.
  27. *
  28. * We can deduce the parameter name based on its value.
  29. *
  30. * @var bool
  31. */
  32. public $noName = false;
  33. /**
  34. * Parameter value.
  35. *
  36. * @var string
  37. */
  38. protected $value;
  39. /**
  40. * Sets up the object.
  41. *
  42. * It's recommended to use the create:: factory method instead.
  43. *
  44. * @param string $name
  45. * @param string $value
  46. */
  47. public function __construct(Document $root, $name, $value = null)
  48. {
  49. $this->root = $root;
  50. if (is_null($name)) {
  51. $this->noName = true;
  52. $this->name = static::guessParameterNameByValue($value);
  53. } else {
  54. $this->name = strtoupper($name);
  55. }
  56. // If guessParameterNameByValue() returns an empty string
  57. // above, we're actually dealing with a parameter that has no value.
  58. // In that case we have to move the value to the name.
  59. if ('' === $this->name) {
  60. $this->noName = false;
  61. $this->name = strtoupper($value);
  62. } else {
  63. $this->setValue($value);
  64. }
  65. }
  66. /**
  67. * Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
  68. *
  69. * Figuring out what the name should have been. Note that a ton of
  70. * these are rather silly in 2014 and would probably rarely be
  71. * used, but we like to be complete.
  72. *
  73. * @param string $value
  74. *
  75. * @return string
  76. */
  77. public static function guessParameterNameByValue($value)
  78. {
  79. switch (strtoupper($value)) {
  80. // Encodings
  81. case '7-BIT':
  82. case 'QUOTED-PRINTABLE':
  83. case 'BASE64':
  84. $name = 'ENCODING';
  85. break;
  86. // Common types
  87. case 'WORK':
  88. case 'HOME':
  89. case 'PREF':
  90. // Delivery Label Type
  91. case 'DOM':
  92. case 'INTL':
  93. case 'POSTAL':
  94. case 'PARCEL':
  95. // Telephone types
  96. case 'VOICE':
  97. case 'FAX':
  98. case 'MSG':
  99. case 'CELL':
  100. case 'PAGER':
  101. case 'BBS':
  102. case 'MODEM':
  103. case 'CAR':
  104. case 'ISDN':
  105. case 'VIDEO':
  106. // EMAIL types (lol)
  107. case 'AOL':
  108. case 'APPLELINK':
  109. case 'ATTMAIL':
  110. case 'CIS':
  111. case 'EWORLD':
  112. case 'INTERNET':
  113. case 'IBMMAIL':
  114. case 'MCIMAIL':
  115. case 'POWERSHARE':
  116. case 'PRODIGY':
  117. case 'TLX':
  118. case 'X400':
  119. // Photo / Logo format types
  120. case 'GIF':
  121. case 'CGM':
  122. case 'WMF':
  123. case 'BMP':
  124. case 'DIB':
  125. case 'PICT':
  126. case 'TIFF':
  127. case 'PDF':
  128. case 'PS':
  129. case 'JPEG':
  130. case 'MPEG':
  131. case 'MPEG2':
  132. case 'AVI':
  133. case 'QTIME':
  134. // Sound Digital Audio Type
  135. case 'WAVE':
  136. case 'PCM':
  137. case 'AIFF':
  138. // Key types
  139. case 'X509':
  140. case 'PGP':
  141. $name = 'TYPE';
  142. break;
  143. // Value types
  144. case 'INLINE':
  145. case 'URL':
  146. case 'CONTENT-ID':
  147. case 'CID':
  148. $name = 'VALUE';
  149. break;
  150. default:
  151. $name = '';
  152. }
  153. return $name;
  154. }
  155. /**
  156. * Updates the current value.
  157. *
  158. * This may be either a single, or multiple strings in an array.
  159. *
  160. * @param string|array $value
  161. */
  162. public function setValue($value)
  163. {
  164. $this->value = $value;
  165. }
  166. /**
  167. * Returns the current value.
  168. *
  169. * This method will always return a string, or null. If there were multiple
  170. * values, it will automatically concatenate them (separated by comma).
  171. *
  172. * @return string|null
  173. */
  174. public function getValue()
  175. {
  176. if (is_array($this->value)) {
  177. return implode(',', $this->value);
  178. } else {
  179. return $this->value;
  180. }
  181. }
  182. /**
  183. * Sets multiple values for this parameter.
  184. */
  185. public function setParts(array $value)
  186. {
  187. $this->value = $value;
  188. }
  189. /**
  190. * Returns all values for this parameter.
  191. *
  192. * If there were no values, an empty array will be returned.
  193. *
  194. * @return array
  195. */
  196. public function getParts()
  197. {
  198. if (is_array($this->value)) {
  199. return $this->value;
  200. } elseif (is_null($this->value)) {
  201. return [];
  202. } else {
  203. return [$this->value];
  204. }
  205. }
  206. /**
  207. * Adds a value to this parameter.
  208. *
  209. * If the argument is specified as an array, all items will be added to the
  210. * parameter value list.
  211. *
  212. * @param string|array $part
  213. */
  214. public function addValue($part)
  215. {
  216. if (is_null($this->value)) {
  217. $this->value = $part;
  218. } else {
  219. $this->value = array_merge((array) $this->value, (array) $part);
  220. }
  221. }
  222. /**
  223. * Checks if this parameter contains the specified value.
  224. *
  225. * This is a case-insensitive match. It makes sense to call this for for
  226. * instance the TYPE parameter, to see if it contains a keyword such as
  227. * 'WORK' or 'FAX'.
  228. *
  229. * @param string $value
  230. *
  231. * @return bool
  232. */
  233. public function has($value)
  234. {
  235. return in_array(
  236. strtolower($value),
  237. array_map('strtolower', (array) $this->value)
  238. );
  239. }
  240. /**
  241. * Turns the object back into a serialized blob.
  242. *
  243. * @return string
  244. */
  245. public function serialize()
  246. {
  247. $value = $this->getParts();
  248. if (0 === count($value)) {
  249. return $this->name.'=';
  250. }
  251. if (Document::VCARD21 === $this->root->getDocumentType() && $this->noName) {
  252. return implode(';', $value);
  253. }
  254. return $this->name.'='.array_reduce(
  255. $value,
  256. function ($out, $item) {
  257. if (!is_null($out)) {
  258. $out .= ',';
  259. }
  260. // If there's no special characters in the string, we'll use the simple
  261. // format.
  262. //
  263. // The list of special characters is defined as:
  264. //
  265. // Any character except CONTROL, DQUOTE, ";", ":", ","
  266. //
  267. // by the iCalendar spec:
  268. // https://tools.ietf.org/html/rfc5545#section-3.1
  269. //
  270. // And we add ^ to that because of:
  271. // https://tools.ietf.org/html/rfc6868
  272. //
  273. // But we've found that iCal (7.0, shipped with OSX 10.9)
  274. // severely trips on + characters not being quoted, so we
  275. // added + as well.
  276. if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
  277. return $out.$item;
  278. } else {
  279. // Enclosing in double-quotes, and using RFC6868 for encoding any
  280. // special characters
  281. $out .= '"'.strtr(
  282. $item,
  283. [
  284. '^' => '^^',
  285. "\n" => '^n',
  286. '"' => '^\'',
  287. ]
  288. ).'"';
  289. return $out;
  290. }
  291. }
  292. );
  293. }
  294. /**
  295. * This method returns an array, with the representation as it should be
  296. * encoded in JSON. This is used to create jCard or jCal documents.
  297. *
  298. * @return array
  299. */
  300. #[\ReturnTypeWillChange]
  301. public function jsonSerialize()
  302. {
  303. return $this->value;
  304. }
  305. /**
  306. * This method serializes the data into XML. This is used to create xCard or
  307. * xCal documents.
  308. *
  309. * @param Xml\Writer $writer XML writer
  310. */
  311. public function xmlSerialize(Xml\Writer $writer): void
  312. {
  313. foreach (explode(',', $this->value) as $value) {
  314. $writer->writeElement('text', $value);
  315. }
  316. }
  317. /**
  318. * Called when this object is being cast to a string.
  319. *
  320. * @return string
  321. */
  322. public function __toString()
  323. {
  324. return (string) $this->getValue();
  325. }
  326. /**
  327. * Returns the iterator for this object.
  328. *
  329. * @return ElementList
  330. */
  331. #[\ReturnTypeWillChange]
  332. public function getIterator()
  333. {
  334. if (!is_null($this->iterator)) {
  335. return $this->iterator;
  336. }
  337. return $this->iterator = new ArrayIterator((array) $this->value);
  338. }
  339. }