Skip to content

Commit

Permalink
Merge pull request #65 from jeroendesloovere/pr/63
Browse files Browse the repository at this point in the history
Pr/63
  • Loading branch information
jeroendesloovere committed May 30, 2016
2 parents 80ae61a + eb7d496 commit 4726aea
Show file tree
Hide file tree
Showing 3 changed files with 471 additions and 0 deletions.
261 changes: 261 additions & 0 deletions src/VCardParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
<?php

namespace JeroenDesloovere\VCard;

/*
* Copyright 2010 Thomas Schaaf <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Changes by: Wouter Admiraal <[email protected]>
* Original code is available at: http://code.google.com/p/zendvcard/
*/

/**
* VCard PHP Class to parse .vcard files.
*
* This class is heavily based on the Zendvcard project (seemingly abandoned),
* which is licensed under the Apache 2.0 license.
* More information can be found at https://code.google.com/archive/p/zendvcard/
*
* @author Thomas Schaaf <[email protected]>
* @author ruzicka.jan
* @author Wouter Admiraal <[email protected]>
*/
class VCardParser
{

/**
* The raw VCard content.
*
* @var string
*/
protected $content;

/**
* The VCard data objects.
*
* @var array
*/
protected $vcardObjects;

/**
* Helper function to parse a file directly.
*
* @param string $filename
*
* @return JeroenDesloovere\VCard\VCardParser
*/
public static function parseFromFile($filename)
{
if (file_exists($filename) && is_readable($filename)) {
return new VCardParser(file_get_contents($filename));
} else {
throw new \RuntimeException(sprintf("File %s is not readable, or doesn't exist.", $filename));
}
}

public function __construct($content)
{
$this->content = $content;
$this->vcardObjects = array();
$this->parse();
}

/**
* Fetch the imported VCard at the specified index.
*
* @throws OutOfBoundsException
*
* @param int $i
*
* @return stdClass
* The card data object.
*/
public function getCardAtIndex($i)
{
if (isset($this->vcardObjects[$i])) {
return $this->vcardObjects[$i];
}
throw new \OutOfBoundsException();
}

/**
* Start the parsing process.
*
* This method will populate the data object.
*/
protected function parse()
{
// Normalize new lines.
$this->content = str_replace(array("\r\n", "\r"), "\n", $this->content);

// RFC2425 5.8.1. Line delimiting and folding
// Unfolding is accomplished by regarding CRLF immediately followed by
// a white space character (namely HTAB ASCII decimal 9 or. SPACE ASCII
// decimal 32) as equivalent to no characters at all (i.e., the CRLF
// and single white space character are removed).
$this->content = preg_replace("/\n(?:[ \t])/", "", $this->content);
$lines = explode("\n", $this->content);

// Parse the VCard, line by line.
foreach ($lines as $line) {
$line = trim($line);

if (strtoupper($line) == "BEGIN:VCARD") {
$cardData = new \stdClass();
} elseif (strtoupper($line) == "END:VCARD") {
$this->vcardObjects[] = $cardData;
} elseif (!empty($line)) {
$type = '';
$value = '';
@list($type, $value) = explode(':', $line, 2);

$types = explode(';', $type);
$element = strtoupper($types[0]);

array_shift($types);
$i = 0;
$rawValue = false;
foreach ($types as $type) {
if (preg_match('/base64/', strtolower($type))) {
$value = base64_decode($value);
unset($types[$i]);
$rawValue = true;
} elseif (preg_match('/encoding=b/', strtolower($type))) {
$value = base64_decode($value);
unset($types[$i]);
$rawValue = true;
} elseif (preg_match('/quoted-printable/', strtolower($type))) {
$value = quoted_printable_decode($value);
unset($types[$i]);
$rawValue = true;
} elseif (strpos(strtolower($type), 'charset=') === 0) {
try {
$value = mb_convert_encoding($value, "UTF-8", substr($type, 8));
} catch (\Exception $e) { }
unset($types[$i]);
}
$i++;
}

switch (strtoupper($element)) {
case 'FN':
$cardData->fullname = $value;
break;
case 'N':
foreach($this->parseName($value) as $key => $val) {
$cardData->{$key} = $val;
}
break;
case 'BDAY':
$cardData->birthday = $this->parseBirthday($value);
break;
case 'ADR':
if (!isset($cardData->address)) {
$cardData->address = array();
}
$key = !empty($types) ? implode(';', $types) : 'WORK;POSTAL';
$cardData->address[$key][] = $this->parseAddress($value);
break;
case 'TEL':
if (!isset($cardData->phone)) {
$cardData->phone = array();
}
$key = !empty($types) ? implode(';', $types) : 'default';
$cardData->phone[$key][] = $value;
break;
case 'EMAIL':
if (!isset($cardData->email)) {
$cardData->email = array();
}
$key = !empty($types) ? implode(';', $types) : 'default';
$cardData->email[$key][] = $value;
break;
case 'REV':
$cardData->revision = $value;
break;
case 'VERSION':
$cardData->version = $value;
break;
case 'ORG':
$cardData->organization = $value;
break;
case 'URL':
if (!isset($cardData->url)) {
$cardData->url = array();
}
$key = !empty($types) ? implode(';', $types) : 'default';
$cardData->url[$key][] = $value;
break;
case 'TITLE':
$cardData->title = $value;
break;
case 'PHOTO':
if ($rawValue) {
$cardData->rawPhoto = $value;
} else {
$cardData->photo = $value;
}
break;
}
}
}
}

protected function parseName($value)
{
@list(
$lastname,
$firstname,
$additional,
$prefix,
$suffix
) = explode(';', $value);
return (object) array(
'lastname' => $lastname,
'firstname' => $firstname,
'additional' => $additional,
'prefix' => $prefix,
'suffix' => $suffix,
);
}

protected function parseBirthday($value)
{
return new \DateTime($value);
}

protected function parseAddress($value)
{
@list(
$name,
$extended,
$street,
$city,
$region,
$zip,
$country,
) = explode(';', $value);
return (object) array(
'name' => $name,
'extended' => $extended,
'street' => $street,
'city' => $city,
'region' => $region,
'zip' => $zip,
'country' => $country,
);
}

}
Loading

0 comments on commit 4726aea

Please sign in to comment.