<?php
namespace Sabre\VObject;
use Sabre\Xml;
use ArrayIterator;
/**
* VObject Parameter.
*
* This class represents a parameter. A parameter is always tied to a property.
* In the case of:
* DTSTART;VALUE=DATE:20101108
* VALUE=DATE would be the parameter name and value.
*
* @copyright Copyright (C) fruux GmbH (https://fruux.com/)
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class Parameter extends Node {
/**
* Parameter name.
*
* @var string
*/
public $name;
/**
* vCard 2.1 allows parameters to be encoded without a name.
*
* We can deduce the parameter name based on it's value.
*
* @var bool
*/
public $noName = false;
/**
* Parameter value.
*
* @var string
*/
protected $value;
/**
* Sets up the object.
*
* It's recommended to use the create:: factory method instead.
*
* @param string $name
* @param string $value
*/
function __construct(Document $root, $name, $value = null) {
$this->name = strtoupper($name);
$this->root = $root;
if (is_null($name)) {
$this->noName = true;
$this->name = static::guessParameterNameByValue($value);
}
// If guessParameterNameByValue() returns an empty string
// above, we're actually dealing with a parameter that has no value.
// In that case we have to move the value to the name.
if ($this->name === '') {
$this->noName = false;
$this->name = strtoupper($value);
} else {
$this->setValue($value);
}
}
/**
* Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
*
* Figuring out what the name should have been. Note that a ton of
* these are rather silly in 2014 and would probably rarely be
* used, but we like to be complete.
*
* @param string $value
*
* @return string
*/
static function guessParameterNameByValue($value) {
switch (strtoupper($value)) {
// Encodings
case '7-BIT' :
case 'QUOTED-PRINTABLE' :
case 'BASE64' :
$name = 'ENCODING';
break;
// Common types
case 'WORK' :
case 'HOME' :
case 'PREF' :
// Delivery Label Type
case 'DOM' :
case 'INTL' :
case 'POSTAL' :
case 'PARCEL' :
// Telephone types
case 'VOICE' :
case 'FAX' :
case 'MSG' :
case 'CELL' :
case 'PAGER' :
case 'BBS' :
case 'MODEM' :
case 'CAR' :
case 'ISDN' :
case 'VIDEO' :
// EMAIL types (lol)
case 'AOL' :
case 'APPLELINK' :
case 'ATTMAIL' :
case 'CIS' :
case 'EWORLD' :
case 'INTERNET' :
case 'IBMMAIL' :
case 'MCIMAIL' :
case 'POWERSHARE' :
case 'PRODIGY' :
case 'TLX' :
case 'X400' :
// Photo / Logo format types
case 'GIF' :
case 'CGM' :
case 'WMF' :
case 'BMP' :
case 'DIB' :
case 'PICT' :
case 'TIFF' :
case 'PDF' :
case 'PS' :
case 'JPEG' :
case 'MPEG' :
case 'MPEG2' :
case 'AVI' :
case 'QTIME' :
// Sound Digital Audio Type
case 'WAVE' :
case 'PCM' :
case 'AIFF' :
// Key types
case 'X509' :
case 'PGP' :
$name = 'TYPE';
break;
// Value types
case 'INLINE' :
case 'URL' :
case 'CONTENT-ID' :
case 'CID' :
$name = 'VALUE';
break;
default:
$name = '';
}
return $name;
}
/**
* Updates the current value.
*
* This may be either a single, or multiple strings in an array.
*
* @param string|array $value
*
* @return void
*/
function setValue($value) {
$this->value = $value;
}
/**
* Returns the current value.
*
* This method will always return a string, or null. If there were multiple
* values, it will automatically concatenate them (separated by comma).
*
* @return string|null
*/
function getValue() {
if (is_array($this->value)) {
return implode(',', $this->value);
} else {
return $this->value;
}
}
/**
* Sets multiple values for this parameter.
*
* @param array $value
*
* @return void
*/
function setParts(array $value) {
$this->value = $value;
}
/**
* Returns all values for this parameter.
*
* If there were no values, an empty array will be returned.
*
* @return array
*/
function getParts() {
if (is_array($this->value)) {
return $this->value;
} elseif (is_null($this->value)) {
return [];
} else {
return [$this->value];
}
}
/**
* Adds a value to this parameter.
*
* If the argument is specified as an array, all items will be added to the
* parameter value list.
*
* @param string|array $part
*
* @return void
*/
function addValue($part) {
if (is_null($this->value)) {
$this->value = $part;
} else {
$this->value = array_merge((array)$this->value, (array)$part);
}
}
/**
* Checks if this parameter contains the specified value.
*
* This is a case-insensitive match. It makes sense to call this for for
* instance the TYPE parameter, to see if it contains a keyword such as
* 'WORK' or 'FAX'.
*
* @param string $value
*
* @return bool
*/
function has($value) {
return in_array(
strtolower($value),
array_map('strtolower', (array)$this->value)
);
}
/**
* Turns the object back into a serialized blob.
*
* @return string
*/
function serialize() {
$value = $this->getParts();
if (count($value) === 0) {
return $this->name . '=';
}
if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
return implode(';', $value);
}
return $this->name . '=' . array_reduce(
$value,
function($out, $item) {
if (!is_null($out)) $out .= ',';
// If there's no special characters in the string, we'll use the simple
// format.
//
// The list of special characters is defined as:
//
// Any character except CONTROL, DQUOTE, ";", ":", ","
//
// by the iCalendar spec:
// https://tools.ietf.org/html/rfc5545#section-3.1
//
// And we add ^ to that because of:
// https://tools.ietf.org/html/rfc6868
//
// But we've found that iCal (7.0, shipped with OSX 10.9)
// severaly trips on + characters not being quoted, so we
// added + as well.
if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
return $out . $item;
} else {
// Enclosing in double-quotes, and using RFC6868 for encoding any
// special characters
$out .= '"' . strtr(
$item,
[
'^' => '^^',
"\n" => '^n',
'"' => '^\'',
]
) . '"';
return $out;
}
}
);
}
/**
* This method returns an array, with the representation as it should be
* encoded in JSON. This is used to create jCard or jCal documents.
*
* @return array
*/
function jsonSerialize() {
return $this->value;
}
/**
* This method serializes the data into XML. This is used to create xCard or
* xCal documents.
*
* @param Xml\Writer $writer XML writer.
*
* @return void
*/
function xmlSerialize(Xml\Writer $writer) {
foreach (explode(',', $this->value) as $value) {
$writer->writeElement('text', $value);
}
}
/**
* Called when this object is being cast to a string.
*
* @return string
*/
function __toString() {
return (string)$this->getValue();
}
/**
* Returns the iterator for this object.
*
* @return ElementList
*/
function getIterator() {
if (!is_null($this->iterator))
return $this->iterator;
return $this->iterator = new ArrayIterator((array)$this->value);
}
}