1001 lines
24 KiB
PHP
1001 lines
24 KiB
PHP
<?php
|
|
|
|
namespace JeroenDesloovere\VCard;
|
|
|
|
/*
|
|
* This file is part of the VCard PHP Class from Jeroen Desloovere.
|
|
*
|
|
* For the full copyright and license information, please view the license
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
use Behat\Transliterator\Transliterator;
|
|
|
|
/**
|
|
* VCard PHP Class to generate .vcard files and save them to a file or output as a download.
|
|
*/
|
|
class VCard
|
|
{
|
|
/**
|
|
* definedElements
|
|
*
|
|
* @var array
|
|
*/
|
|
private $definedElements;
|
|
|
|
/**
|
|
* Filename
|
|
*
|
|
* @var string
|
|
*/
|
|
private $filename;
|
|
|
|
/**
|
|
* Save Path
|
|
*
|
|
* @var string
|
|
*/
|
|
private $savePath = null;
|
|
|
|
/**
|
|
* Multiple properties for element allowed
|
|
*
|
|
* @var array
|
|
*/
|
|
private $multiplePropertiesForElementAllowed = [
|
|
'email',
|
|
'address',
|
|
'phoneNumber',
|
|
'url',
|
|
'label'
|
|
];
|
|
|
|
/**
|
|
* Properties
|
|
*
|
|
* @var array
|
|
*/
|
|
private $properties;
|
|
|
|
/**
|
|
* Default Charset
|
|
*
|
|
* @var string
|
|
*/
|
|
public $charset = 'utf-8';
|
|
|
|
/**
|
|
* Add address
|
|
*
|
|
* @param string [optional] $name
|
|
* @param string [optional] $extended
|
|
* @param string [optional] $street
|
|
* @param string [optional] $city
|
|
* @param string [optional] $region
|
|
* @param string [optional] $zip
|
|
* @param string [optional] $country
|
|
* @param string [optional] $type
|
|
* $type may be DOM | INTL | POSTAL | PARCEL | HOME | WORK
|
|
* or any combination of these: e.g. "WORK;PARCEL;POSTAL"
|
|
* @return $this
|
|
*/
|
|
public function addAddress(
|
|
$name = '',
|
|
$extended = '',
|
|
$street = '',
|
|
$city = '',
|
|
$region = '',
|
|
$zip = '',
|
|
$country = '',
|
|
$type = 'WORK;POSTAL'
|
|
) {
|
|
// init value
|
|
$value = $name . ';' . $extended . ';' . $street . ';' . $city . ';' . $region . ';' . $zip . ';' . $country;
|
|
|
|
// set property
|
|
$this->setProperty(
|
|
'address',
|
|
'ADR' . (($type != '') ? ';' . $type : '') . $this->getCharsetString(),
|
|
$value
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add birthday
|
|
*
|
|
* @param string $date Format is YYYY-MM-DD
|
|
* @return $this
|
|
*/
|
|
public function addBirthday($date)
|
|
{
|
|
$this->setProperty(
|
|
'birthday',
|
|
'BDAY',
|
|
$date
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add company
|
|
*
|
|
* @param string $company
|
|
* @param string $department
|
|
* @return $this
|
|
*/
|
|
public function addCompany($company, $department = '')
|
|
{
|
|
$this->setProperty(
|
|
'company',
|
|
'ORG' . $this->getCharsetString(),
|
|
$company
|
|
. ($department != '' ? ';' . $department : '')
|
|
);
|
|
|
|
// if filename is empty, add to filename
|
|
if ($this->filename === null) {
|
|
$this->setFilename($company);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add email
|
|
*
|
|
* @param string $address The e-mail address
|
|
* @param string [optional] $type The type of the email address
|
|
* $type may be PREF | WORK | HOME
|
|
* or any combination of these: e.g. "PREF;WORK"
|
|
* @return $this
|
|
*/
|
|
public function addEmail($address, $type = '')
|
|
{
|
|
$this->setProperty(
|
|
'email',
|
|
'EMAIL;INTERNET' . (($type != '') ? ';' . $type : ''),
|
|
$address
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add jobtitle
|
|
*
|
|
* @param string $jobtitle The jobtitle for the person.
|
|
* @return $this
|
|
*/
|
|
public function addJobtitle($jobtitle)
|
|
{
|
|
$this->setProperty(
|
|
'jobtitle',
|
|
'TITLE' . $this->getCharsetString(),
|
|
$jobtitle
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a label
|
|
*
|
|
* @param string $label
|
|
* @param string $type
|
|
*
|
|
* @return $this
|
|
*/
|
|
public function addLabel($label, $type = '')
|
|
{
|
|
$this->setProperty(
|
|
'label',
|
|
'LABEL' . ($type !== '' ? ';' . $type : '') . $this->getCharsetString(),
|
|
$label
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add role
|
|
*
|
|
* @param string $role The role for the person.
|
|
* @return $this
|
|
*/
|
|
public function addRole($role)
|
|
{
|
|
$this->setProperty(
|
|
'role',
|
|
'ROLE' . $this->getCharsetString(),
|
|
$role
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a photo or logo (depending on property name)
|
|
*
|
|
* @param string $property LOGO|PHOTO
|
|
* @param string $url image url or filename
|
|
* @param bool $include Do we include the image in our vcard or not?
|
|
* @param string $element The name of the element to set
|
|
* @throws VCardException
|
|
*/
|
|
private function addMedia($property, $url, $element, $include = true)
|
|
{
|
|
$mimeType = null;
|
|
|
|
//Is this URL for a remote resource?
|
|
if (filter_var($url, FILTER_VALIDATE_URL) !== false) {
|
|
$headers = get_headers($url, 1);
|
|
|
|
if (array_key_exists('Content-Type', $headers)) {
|
|
$mimeType = $headers['Content-Type'];
|
|
if (is_array($mimeType)) {
|
|
$mimeType = end($mimeType);
|
|
}
|
|
}
|
|
} else {
|
|
//Local file, so inspect it directly
|
|
$mimeType = mime_content_type($url);
|
|
}
|
|
if (strpos($mimeType, ';') !== false) {
|
|
$mimeType = strstr($mimeType, ';', true);
|
|
}
|
|
if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') {
|
|
throw VCardException::invalidImage();
|
|
}
|
|
$fileType = strtoupper(substr($mimeType, 6));
|
|
|
|
if ($include) {
|
|
if ((bool) ini_get('allow_url_fopen') === true) {
|
|
$value = file_get_contents($url);
|
|
} else {
|
|
$curl = curl_init();
|
|
curl_setopt($curl, CURLOPT_URL, $url);
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
$value = curl_exec($curl);
|
|
curl_close($curl);
|
|
}
|
|
|
|
if (!$value) {
|
|
throw VCardException::emptyURL();
|
|
}
|
|
|
|
$value = base64_encode($value);
|
|
$property .= ";ENCODING=b;TYPE=" . $fileType;
|
|
} else {
|
|
if (filter_var($url, FILTER_VALIDATE_URL) !== false) {
|
|
$propertySuffix = ';VALUE=URL';
|
|
$propertySuffix .= ';TYPE=' . strtoupper($fileType);
|
|
|
|
$property = $property . $propertySuffix;
|
|
$value = $url;
|
|
} else {
|
|
$value = $url;
|
|
}
|
|
}
|
|
|
|
$this->setProperty(
|
|
$element,
|
|
$property,
|
|
$value
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add a photo or logo (depending on property name)
|
|
*
|
|
* @param string $property LOGO|PHOTO
|
|
* @param string $content image content
|
|
* @param string $element The name of the element to set
|
|
*/
|
|
private function addMediaContent($property, $content, $element)
|
|
{
|
|
$finfo = new \finfo();
|
|
$mimeType = $finfo->buffer($content, FILEINFO_MIME_TYPE);
|
|
|
|
if (strpos($mimeType, ';') !== false) {
|
|
$mimeType = strstr($mimeType, ';', true);
|
|
}
|
|
if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') {
|
|
throw VCardException::invalidImage();
|
|
}
|
|
$fileType = strtoupper(substr($mimeType, 6));
|
|
|
|
$content = base64_encode($content);
|
|
$property .= ";ENCODING=b;TYPE=" . $fileType;
|
|
|
|
$this->setProperty(
|
|
$element,
|
|
$property,
|
|
$content
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add name
|
|
*
|
|
* @param string [optional] $lastName
|
|
* @param string [optional] $firstName
|
|
* @param string [optional] $additional
|
|
* @param string [optional] $prefix
|
|
* @param string [optional] $suffix
|
|
* @return $this
|
|
*/
|
|
public function addName(
|
|
$lastName = '',
|
|
$firstName = '',
|
|
$additional = '',
|
|
$prefix = '',
|
|
$suffix = ''
|
|
) {
|
|
// define values with non-empty values
|
|
$values = array_filter([
|
|
$prefix,
|
|
$firstName,
|
|
$additional,
|
|
$lastName,
|
|
$suffix,
|
|
]);
|
|
|
|
// define filename
|
|
$this->setFilename($values);
|
|
|
|
// set property
|
|
$property = $lastName . ';' . $firstName . ';' . $additional . ';' . $prefix . ';' . $suffix;
|
|
$this->setProperty(
|
|
'name',
|
|
'N' . $this->getCharsetString(),
|
|
$property
|
|
);
|
|
|
|
// is property FN set?
|
|
if (!$this->hasProperty('FN')) {
|
|
// set property
|
|
$this->setProperty(
|
|
'fullname',
|
|
'FN' . $this->getCharsetString(),
|
|
trim(implode(' ', $values))
|
|
);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add note
|
|
*
|
|
* @param string $note
|
|
* @return $this
|
|
*/
|
|
public function addNote($note)
|
|
{
|
|
$this->setProperty(
|
|
'note',
|
|
'NOTE' . $this->getCharsetString(),
|
|
$note
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add categories
|
|
*
|
|
* @param array $categories
|
|
* @return $this
|
|
*/
|
|
public function addCategories($categories)
|
|
{
|
|
$this->setProperty(
|
|
'categories',
|
|
'CATEGORIES' . $this->getCharsetString(),
|
|
trim(implode(',', $categories))
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add phone number
|
|
*
|
|
* @param string $number
|
|
* @param string [optional] $type
|
|
* Type may be PREF | WORK | HOME | VOICE | FAX | MSG |
|
|
* CELL | PAGER | BBS | CAR | MODEM | ISDN | VIDEO
|
|
* or any senseful combination, e.g. "PREF;WORK;VOICE"
|
|
* @return $this
|
|
*/
|
|
public function addPhoneNumber($number, $type = '')
|
|
{
|
|
$this->setProperty(
|
|
'phoneNumber',
|
|
'TEL' . (($type != '') ? ';' . $type : ''),
|
|
$number
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add Logo
|
|
*
|
|
* @param string $url image url or filename
|
|
* @param bool $include Include the image in our vcard?
|
|
* @return $this
|
|
*/
|
|
public function addLogo($url, $include = true)
|
|
{
|
|
$this->addMedia(
|
|
'LOGO',
|
|
$url,
|
|
'logo',
|
|
$include
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add Logo content
|
|
*
|
|
* @param string $content image content
|
|
* @return $this
|
|
*/
|
|
public function addLogoContent($content)
|
|
{
|
|
$this->addMediaContent(
|
|
'LOGO',
|
|
$content,
|
|
'logo'
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add Photo
|
|
*
|
|
* @param string $url image url or filename
|
|
* @param bool $include Include the image in our vcard?
|
|
* @return $this
|
|
*/
|
|
public function addPhoto($url, $include = true)
|
|
{
|
|
$this->addMedia(
|
|
'PHOTO',
|
|
$url,
|
|
'photo',
|
|
$include
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add Photo content
|
|
*
|
|
* @param string $content image content
|
|
* @return $this
|
|
*/
|
|
public function addPhotoContent($content)
|
|
{
|
|
$this->addMediaContent(
|
|
'PHOTO',
|
|
$content,
|
|
'photo'
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add URL
|
|
*
|
|
* @param string $url
|
|
* @param string [optional] $type Type may be WORK | HOME
|
|
* @return $this
|
|
*/
|
|
public function addURL($url, $type = '')
|
|
{
|
|
$this->setProperty(
|
|
'url',
|
|
'URL' . (($type != '') ? ';' . $type : ''),
|
|
$url
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Build VCard (.vcf)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function buildVCard()
|
|
{
|
|
// init string
|
|
$string = "BEGIN:VCARD\r\n";
|
|
$string .= "VERSION:3.0\r\n";
|
|
$string .= "REV:" . date("Y-m-d") . "T" . date("H:i:s") . "Z\r\n";
|
|
|
|
// loop all properties
|
|
$properties = $this->getProperties();
|
|
foreach ($properties as $property) {
|
|
// add to string
|
|
$string .= $this->fold($property['key'] . ':' . $this->escape($property['value']) . "\r\n");
|
|
}
|
|
|
|
// add to string
|
|
$string .= "END:VCARD\r\n";
|
|
|
|
// return
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Build VCalender (.ics) - Safari (< iOS 8) can not open .vcf files, so we have build a workaround.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function buildVCalendar()
|
|
{
|
|
// init dates
|
|
$dtstart = date("Ymd") . "T" . date("Hi") . "00";
|
|
$dtend = date("Ymd") . "T" . date("Hi") . "01";
|
|
|
|
// init string
|
|
$string = "BEGIN:VCALENDAR\n";
|
|
$string .= "VERSION:2.0\n";
|
|
$string .= "BEGIN:VEVENT\n";
|
|
$string .= "DTSTART;TZID=Europe/London:" . $dtstart . "\n";
|
|
$string .= "DTEND;TZID=Europe/London:" . $dtend . "\n";
|
|
$string .= "SUMMARY:Click attached contact below to save to your contacts\n";
|
|
$string .= "DTSTAMP:" . $dtstart . "Z\n";
|
|
$string .= "ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/directory;\n";
|
|
$string .= " X-APPLE-FILENAME=" . $this->getFilename() . "." . $this->getFileExtension() . ":\n";
|
|
|
|
// base64 encode it so that it can be used as an attachemnt to the "dummy" calendar appointment
|
|
$b64vcard = base64_encode($this->buildVCard());
|
|
|
|
// chunk the single long line of b64 text in accordance with RFC2045
|
|
// (and the exact line length determined from the original .ics file exported from Apple calendar
|
|
$b64mline = chunk_split($b64vcard, 74, "\n");
|
|
|
|
// need to indent all the lines by 1 space for the iphone (yes really?!!)
|
|
$b64final = preg_replace('/(.+)/', ' $1', $b64mline);
|
|
$string .= $b64final;
|
|
|
|
// output the correctly formatted encoded text
|
|
$string .= "END:VEVENT\n";
|
|
$string .= "END:VCALENDAR\n";
|
|
|
|
// return
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Returns the browser user agent string.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getUserAgent()
|
|
{
|
|
if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
|
|
$browser = strtolower($_SERVER['HTTP_USER_AGENT']);
|
|
} else {
|
|
$browser = 'unknown';
|
|
}
|
|
|
|
return $browser;
|
|
}
|
|
|
|
/**
|
|
* Decode
|
|
*
|
|
* @param string $value The value to decode
|
|
* @return string decoded
|
|
*/
|
|
private function decode($value)
|
|
{
|
|
// convert cyrlic, greek or other caracters to ASCII characters
|
|
return Transliterator::transliterate($value);
|
|
}
|
|
|
|
/**
|
|
* Download a vcard or vcal file to the browser.
|
|
*/
|
|
public function download()
|
|
{
|
|
// define output
|
|
$output = $this->getOutput();
|
|
|
|
foreach ($this->getHeaders(false) as $header) {
|
|
header($header);
|
|
}
|
|
|
|
// echo the output and it will be a download
|
|
echo $output;
|
|
}
|
|
|
|
/**
|
|
* Fold a line according to RFC2425 section 5.8.1.
|
|
*
|
|
* @link http://tools.ietf.org/html/rfc2425#section-5.8.1
|
|
* @param string $text
|
|
* @return mixed
|
|
*/
|
|
protected function fold($text)
|
|
{
|
|
if (strlen($text) <= 75) {
|
|
return $text;
|
|
}
|
|
|
|
// The chunk_split_unicode creates a huge memory footprint when used on long strings (EG photos are base64 10MB results in > 1GB memory usage)
|
|
// So check if the string is ASCII (7 bit) and if it is use the built in way RE: https://github.com/jeroendesloovere/vcard/issues/153
|
|
if ($this->is_ascii($text)) {
|
|
return substr(chunk_split($text, 75, "\r\n "), 0, -3);
|
|
}
|
|
|
|
// split, wrap and trim trailing separator
|
|
return substr($this->chunk_split_unicode($text, 75, "\r\n "), 0, -3);
|
|
}
|
|
|
|
|
|
/**
|
|
* Determine if string is pure 7bit ascii
|
|
* @link https://pageconfig.com/post/how-to-validate-ascii-text-in-php
|
|
*
|
|
* @param string $string
|
|
* @return bool
|
|
*/
|
|
protected function is_ascii($string = '' ) {
|
|
$num = 0;
|
|
while( isset( $string[$num] ) ) {
|
|
if( ord( $string[$num] ) & 0x80 ) {
|
|
return false;
|
|
}
|
|
$num++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* multibyte word chunk split
|
|
* @link http://php.net/manual/en/function.chunk-split.php#107711
|
|
*
|
|
* @param string $body The string to be chunked.
|
|
* @param integer $chunklen The chunk length.
|
|
* @param string $end The line ending sequence.
|
|
* @return string Chunked string
|
|
*/
|
|
protected function chunk_split_unicode($body, $chunklen = 76, $end = "\r\n")
|
|
{
|
|
$array = array_chunk(
|
|
preg_split("//u", $body, -1, PREG_SPLIT_NO_EMPTY), $chunklen);
|
|
$body = "";
|
|
foreach ($array as $item) {
|
|
$body .= join("", $item) . $end;
|
|
}
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* Escape newline characters according to RFC2425 section 5.8.4.
|
|
*
|
|
* @link http://tools.ietf.org/html/rfc2425#section-5.8.4
|
|
* @param string $text
|
|
* @return string
|
|
*/
|
|
protected function escape($text)
|
|
{
|
|
if ($text === null) {
|
|
return null;
|
|
}
|
|
|
|
$text = str_replace("\r\n", "\\n", $text);
|
|
$text = str_replace("\n", "\\n", $text);
|
|
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Get output as string
|
|
* @deprecated in the future
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get()
|
|
{
|
|
return $this->getOutput();
|
|
}
|
|
|
|
/**
|
|
* Get charset
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCharset()
|
|
{
|
|
return $this->charset;
|
|
}
|
|
|
|
/**
|
|
* Get charset string
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCharsetString()
|
|
{
|
|
return ';CHARSET=' . $this->charset;
|
|
}
|
|
|
|
/**
|
|
* Get content type
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getContentType()
|
|
{
|
|
return ($this->isIOS7()) ?
|
|
'text/x-vcalendar' : 'text/x-vcard';
|
|
}
|
|
|
|
/**
|
|
* Get filename
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getFilename()
|
|
{
|
|
if (!$this->filename) {
|
|
return 'unknown';
|
|
}
|
|
|
|
return $this->filename;
|
|
}
|
|
|
|
/**
|
|
* Get file extension
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getFileExtension()
|
|
{
|
|
return ($this->isIOS7()) ?
|
|
'ics' : 'vcf';
|
|
}
|
|
|
|
/**
|
|
* Get headers
|
|
*
|
|
* @param bool $asAssociative
|
|
* @return array
|
|
*/
|
|
public function getHeaders($asAssociative)
|
|
{
|
|
$contentType = $this->getContentType() . '; charset=' . $this->getCharset();
|
|
$contentDisposition = 'attachment; filename=' . $this->getFilename() . '.' . $this->getFileExtension();
|
|
$contentLength = mb_strlen($this->getOutput(), '8bit');
|
|
$connection = 'close';
|
|
|
|
if ((bool)$asAssociative) {
|
|
return [
|
|
'Content-type' => $contentType,
|
|
'Content-Disposition' => $contentDisposition,
|
|
'Content-Length' => $contentLength,
|
|
'Connection' => $connection,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'Content-type: ' . $contentType,
|
|
'Content-Disposition: ' . $contentDisposition,
|
|
'Content-Length: ' . $contentLength,
|
|
'Connection: ' . $connection,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get output as string
|
|
* iOS devices (and safari < iOS 8 in particular) can not read .vcf (= vcard) files.
|
|
* So I build a workaround to build a .ics (= vcalender) file.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getOutput()
|
|
{
|
|
$output = ($this->isIOS7()) ?
|
|
$this->buildVCalendar() : $this->buildVCard();
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Get properties
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getProperties()
|
|
{
|
|
return $this->properties;
|
|
}
|
|
|
|
/**
|
|
* Has property
|
|
*
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public function hasProperty($key)
|
|
{
|
|
$properties = $this->getProperties();
|
|
|
|
foreach ($properties as $property) {
|
|
if ($property['key'] === $key && $property['value'] !== '') {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Is iOS - Check if the user is using an iOS-device
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isIOS()
|
|
{
|
|
// get user agent
|
|
$browser = $this->getUserAgent();
|
|
|
|
return (strpos($browser, 'iphone') || strpos($browser, 'ipod') || strpos($browser, 'ipad'));
|
|
}
|
|
|
|
/**
|
|
* Is iOS less than 7 (should cal wrapper be returned)
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isIOS7()
|
|
{
|
|
return ($this->isIOS() && $this->shouldAttachmentBeCal());
|
|
}
|
|
|
|
/**
|
|
* Save to a file
|
|
*
|
|
* @return void
|
|
*/
|
|
public function save()
|
|
{
|
|
$file = $this->getFilename() . '.' . $this->getFileExtension();
|
|
|
|
// Add save path if given
|
|
if (null !== $this->savePath) {
|
|
$file = $this->savePath . $file;
|
|
}
|
|
|
|
file_put_contents(
|
|
$file,
|
|
$this->getOutput()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Set charset
|
|
*
|
|
* @param mixed $charset
|
|
* @return void
|
|
*/
|
|
public function setCharset($charset)
|
|
{
|
|
$this->charset = $charset;
|
|
}
|
|
|
|
/**
|
|
* Set filename
|
|
*
|
|
* @param mixed $value
|
|
* @param bool $overwrite [optional] Default overwrite is true
|
|
* @param string $separator [optional] Default separator is an underscore '_'
|
|
* @return void
|
|
*/
|
|
public function setFilename($value, $overwrite = true, $separator = '_')
|
|
{
|
|
// recast to string if $value is array
|
|
if (is_array($value)) {
|
|
$value = implode($separator, $value);
|
|
}
|
|
|
|
// trim unneeded values
|
|
$value = trim($value, $separator);
|
|
|
|
// remove all spaces
|
|
$value = preg_replace('/\s+/', $separator, $value);
|
|
|
|
// if value is empty, stop here
|
|
if (empty($value)) {
|
|
return;
|
|
}
|
|
|
|
// decode value + lowercase the string
|
|
$value = strtolower($this->decode($value));
|
|
|
|
// urlize this part
|
|
$value = Transliterator::urlize($value);
|
|
|
|
// overwrite filename or add to filename using a prefix in between
|
|
$this->filename = ($overwrite) ?
|
|
$value : $this->filename . $separator . $value;
|
|
}
|
|
|
|
/**
|
|
* Set the save path directory
|
|
*
|
|
* @param string $savePath Save Path
|
|
* @throws VCardException
|
|
*/
|
|
public function setSavePath($savePath)
|
|
{
|
|
if (!is_dir($savePath)) {
|
|
throw VCardException::outputDirectoryNotExists();
|
|
}
|
|
|
|
// Add trailing directory separator the save path
|
|
if (substr($savePath, -1) != DIRECTORY_SEPARATOR) {
|
|
$savePath .= DIRECTORY_SEPARATOR;
|
|
}
|
|
|
|
$this->savePath = $savePath;
|
|
}
|
|
|
|
/**
|
|
* Set property
|
|
*
|
|
* @param string $element The element name you want to set, f.e.: name, email, phoneNumber, ...
|
|
* @param string $key
|
|
* @param string $value
|
|
* @throws VCardException
|
|
*/
|
|
private function setProperty($element, $key, $value)
|
|
{
|
|
if (!in_array($element, $this->multiplePropertiesForElementAllowed)
|
|
&& isset($this->definedElements[$element])
|
|
) {
|
|
throw VCardException::elementAlreadyExists($element);
|
|
}
|
|
|
|
// we define that we set this element
|
|
$this->definedElements[$element] = true;
|
|
|
|
// adding property
|
|
$this->properties[] = [
|
|
'key' => $key,
|
|
'value' => $value
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Checks if we should return vcard in cal wrapper
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function shouldAttachmentBeCal()
|
|
{
|
|
$browser = $this->getUserAgent();
|
|
|
|
$matches = [];
|
|
preg_match('/os (\d+)_(\d+)\s+/', $browser, $matches);
|
|
$version = isset($matches[1]) ? ((int)$matches[1]) : 999;
|
|
|
|
return ($version < 8);
|
|
}
|
|
}
|