From 3d7bd6191ac29c7f517113e10c6d219acf79ba1c Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Mon, 9 May 2011 17:58:43 +0300
Subject: [PATCH 01/44] first commit
---
README | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README
diff --git a/README b/README
new file mode 100644
index 00000000..e69de29b
From 8512b993f423604492d6bb0a69ca24373db3156a Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Tue, 10 May 2011 21:41:13 +0300
Subject: [PATCH 02/44] First real commit
---
parser_class2.php | 125 ++++++++++++++++++++++++++++++++++++++++++++++
test.php | 8 +++
2 files changed, 133 insertions(+)
create mode 100644 parser_class2.php
create mode 100644 test.php
diff --git a/parser_class2.php b/parser_class2.php
new file mode 100644
index 00000000..f721b038
--- /dev/null
+++ b/parser_class2.php
@@ -0,0 +1,125 @@
+
+';
+print_r($parse->vCards);
+echo '';
+
+ /**
+ *
+ * Class parses vCard file and returns array with information form vCard.
+ *
+ * @access public
+ *
+ * @return array with vCards
+ *
+ */
+class vCard{
+
+ public $mode; //single, multiple, error
+ public $vCards = array();
+
+ /**
+ *
+ * Functions which reads text line by line from file
+ * and puts it into formatted array
+ *
+ * @access public
+ *
+ * @return array with one or multiple vCards()
+ *
+ */
+ public function vCard($path){
+ $lines = file($path);
+ $card = array();
+ //$counter = 0;
+
+ foreach($lines as $line){
+
+ $pos=strpos($line, 'BEGIN:VCARD');
+ if($pos !== false){
+ $vCard = array();
+ continue;
+ }
+
+ $pos = strpos($line,':');
+
+ if($pos !== false){
+ $pos2 = strpos($line,'END:VCARD');
+ if($pos2 !== false){
+ $this->vCards[] = $vCard;
+ continue;
+ }
+ $marker=$this->escape(substr($line,0,$pos));
+ $line_end=$this->escape(substr($line,$pos+1,strlen($line)));
+ $pos3=strpos($marker,';');
+
+ if(strpos($marker,';')!==false){
+ $a=$this->Qcode($marker, $line_end);
+ $vCard[$a[0]]=$a[1];
+ }
+ else{
+ $vCard[$marker] = $line_end;
+ }
+
+ }else{
+ $a=$this->Qcode($marker, $line_end);
+ $vCard[$a[0]] = $vCard[$a[0]] . $this->escape($line);
+
+ }
+ }
+ }
+ /**
+ *
+ * Function checks if in string is any formatting rule, and
+ * if there is any then returns cleared marker and formatted string
+ * @access public
+ *
+ * @return cleared text string
+ * @return formated marker
+ *
+ * @see vCard()
+ *
+ */
+ public function Qcode($marker,$line_end){
+ $exploded = explode (';',$marker);
+ if (in_array('QUOTED-PRINTABLE',$exploded)){
+ $returnam = array(substr($marker,0,strpos($marker,';')),trim(quoted_printable_decode($line_end)));
+ return $returnam;
+ }
+ else{
+ $returnam = array(substr($marker,0,strpos($marker,';')),trim($line_end));
+ return $returnam;
+ }
+
+ }
+
+
+ /**
+ *
+ * Functions which clears text from unneccessary symbols
+ *
+ * @access public
+ *
+ * @return cleared text string
+ *
+ * @see vCard()
+ *
+ */
+
+ public function escape(&$text){
+ $text = str_replace('\:', ':', $text);
+ $text = str_replace('\;', ';', $text);
+ $text = str_replace(';;', '', $text);
+ $text = str_replace('\,', ',', $text);
+ $text = str_replace("\n", "", $text);
+
+
+ return $text;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/test.php b/test.php
new file mode 100644
index 00000000..dc1dead3
--- /dev/null
+++ b/test.php
@@ -0,0 +1,8 @@
+
\ No newline at end of file
From 2e2bc624fee182a6170b6a32e5a7d2770f65fafa Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Tue, 10 May 2011 21:42:36 +0300
Subject: [PATCH 03/44] First real commit
---
parser_class.php | 327 +++++++++++++++++++++++++++++++++++++++++++++++
test.vcf | 8 ++
2 files changed, 335 insertions(+)
create mode 100644 parser_class.php
create mode 100644 test.vcf
diff --git a/parser_class.php b/parser_class.php
new file mode 100644
index 00000000..1caf888f
--- /dev/null
+++ b/parser_class.php
@@ -0,0 +1,327 @@
+
+';
+print_r($parse->vCards);
+
+
+echo '';
+
+ /**
+ *
+ * Class parses vCard file and returns array with information form vCard.
+ *
+ * @access public
+ *
+ * @return array with vCards
+ *
+ */
+class vCard{
+
+ public $mode; //single, multiple, error
+ public $vCards = array();
+ public $marker0;
+ /**
+ *
+ * Functions which reads text line by line from file
+ * and puts it into formatted array
+ *
+ * @access public
+ *
+ * @return array with one or multiple vCards()
+ *
+ */
+ public function __construct($path){
+ $lines = file($path);
+ $card = array();
+ $remember='';
+ foreach($lines as $line){
+
+ $pos = strpos($line,':');
+ $end=strlen(trim($line));
+ if(substr(trim($line),$end-1, $end)=='='){
+ $remember=$line;
+ continue;
+ }else{
+ $remember='';
+ }
+ if($remember=''){
+
+ //if it is beginning of card - skip
+ $pos=strpos($line, 'BEGIN:VCARD');
+ if($pos !== false){
+ $vCard = array();
+ continue;
+ }
+
+ }else{
+
+ $line=$line.$remember;
+ $remember='';
+
+ //if it is end of card - skip
+ if($pos !== false){
+ $pos2 = strpos($line,'END:VCARD');
+ if($pos2 !== false){
+ $this->vCards[] = $vCard;
+ $vCard = array();
+ continue;
+ }
+
+ //clear marker and line
+
+ $marker=$this->escape(substr($line,0,$pos));
+ $this->marker0 = $marker;
+ $line_end=$this->escape(substr($line,$pos+1,strlen($line)));
+ $pos3=strpos($marker,';');
+
+
+ //if there are arguments like N;Type=Work: John Smith
+ if(strpos($marker,';')!==false){
+ $newvar=$this->split($marker,$line_end);
+ $this->insert($newvar,$marker,$pos3,$vCard);
+
+ }
+ else// if there are no arguments like FN:John Smith
+ {
+ $newvar=$this->split($marker,$line_end);
+ $this->insert($newvar,$marker,strlen($marker),$vCard);
+
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ *
+ * Functions which inserts data into array
+ *
+ *
+ * @access public
+ *
+ * @return nothing
+ *
+ */
+
+public function insert($newvar,$marker,$pos3,&$vCard){
+
+ if(is_array($newvar)){
+ while (list($key, $value) = each($newvar)) {
+
+ if(!isset($vCard[substr($marker,0,$pos3)])){
+ $vCard[substr($marker,0,$pos3)][$key]=$value;
+
+ }
+
+ else{
+
+ if(is_array($vCard[substr($marker,0,$pos3)]))//if already tehre are such kid info
+ {
+ if(isset($vCard[substr($marker,0,$pos3)][$key])){
+ if(count($vCard[substr($marker,0,$pos3)][$key])==1)
+ {
+ $mas=$vCard[substr($marker,0,$pos3)][$key];
+ $vCard[substr($marker,0,$pos3)][$key]=array('1'=>$value);
+ $vCard[substr($marker,0,$pos3)][$key][]=$mas;
+ }
+ else{
+ $vCard[substr($marker,0,$pos3)][$key][]=$value;
+ }
+ }
+ else
+ {
+ $vCard[substr($marker,0,$pos3)][$key]=$value;
+ }
+
+
+ }
+ else{
+ $perms= $vCard[substr($marker,0,$pos3)];
+ $vCard[substr($marker,0,$pos3)][$key]=$value;
+ $vCard[substr($marker,0,$pos3)][]=$perms;
+
+ }
+
+ }
+
+ }
+ }
+ else
+ {
+ if($newvar!=''){
+ $marker=substr($marker,0, $pos3);
+ $vCard[$marker]=$newvar;
+ }
+
+ }
+
+ return;
+}
+
+ /**
+ *
+ * Functions which clears text from unneccessary symbols
+ *
+ * @access public
+ *
+ * @return cleared text string
+ *
+ * @see vCard()
+ *
+ */
+
+ public function escape(&$text){
+ $text = str_replace('\:', ':', $text);
+ $text = str_replace('\;', ';', $text);
+ $text = str_replace(';;', ';', $text);
+ $text = str_replace('\,', ',', $text);
+ $text = str_replace("\n", "", $text);
+ return $text;
+ }
+
+ /**
+ *
+ * Functions which escape text from last symbols
+ *
+ * @access public
+ *
+ * @return string with text
+ *
+ */
+ public function finalescape(&$text){
+ $text = str_replace(";", "", $text);
+ $text = str_replace(";;", "", $text);
+ return $text;
+ }
+
+ /**
+ *
+ * Functions which splits name and address into
+ * multiple fields
+ *
+ * @access public
+ *
+ * @return array with values
+ *
+ */
+
+
+ public function split($marker,$line_end)
+ {
+ $pos=strpos($marker,';');
+ if($pos!==false){
+ $marker2=substr($marker,0, $pos);
+ }
+ else{
+ $marker2=$marker;
+ }
+
+ switch ($marker2) {
+ case 'N'://N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Dombrovskis;J=C4=81nis;;;
+ $exploded = explode (';',$marker);
+ $exploded2 = explode (';',$line_end);
+ $exploded2= array_filter($exploded2);
+ if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded)){
+ $returnam = array('LastName'=>trim(quoted_printable_decode($exploded2[0])),'Name'=>trim(quoted_printable_decode($exploded2[1])));
+ }
+ else{
+ $returnam = array('LastName'=>trim($exploded2[0]),'Name'=>trim($exploded2[1]));
+ }
+ return $returnam;
+ break;
+ case 'ADR'://ADR;TYPE=HOME:;;Eksporta 12-256;Riga;;;Latvia
+
+
+ $line_end =substr($line_end,1,strlen($line_end));
+ $line_end = str_replace(';', ' ', $line_end);
+ $exploded2= explode(';',$marker);
+ if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded2)){
+ $returnam = trim(quoted_printable_decode($line_end));
+ }
+ else{
+ $returnam = trim($line_end);
+ }
+
+ return $returnam;
+ break;
+ case 'URL':
+ $types=substr($this->marker0,9,trim(strlen($this->marker0)));
+ $types=explode(',',$types);
+ foreach($types as $ty)
+ {
+ $returnam[$ty]=trim($line_end);
+ }
+ return $returnam;
+ case 'TEL'://TEL;TYPE=WORK:+371 29594419
+ $types=substr($this->marker0,9,trim(strlen($this->marker0)));
+ $types=explode(',',$types);
+ foreach($types as $ty)
+ {
+ $returnam[$ty]=trim($line_end);
+ }
+
+ return $returnam;
+ break;
+ case 'EMAIL'://EMAIL;TYPE=WORK,INTERNET:martins@sales.lv
+ $types=substr($this->marker0,11,trim(strlen($this->marker0)));
+ $types=explode(',',$types);
+ foreach($types as $ty)
+ {
+ $returnam[$ty]=trim($line_end);
+ }
+
+ return $returnam;
+ break;
+ case 'X-TWITTER':
+ $types=substr($this->marker0,15,trim(strlen($this->marker0)));
+ $types=explode(',',$types);
+ foreach($types as $ty)
+ {
+ $returnam[$ty]=trim($line_end);
+ }
+ return $returnam;
+ break;
+ case 'X-SKYPE':
+ $types=substr($this->marker0,13,trim(strlen($this->marker0)));
+ $types=explode(',',$types);
+ foreach($types as $ty)
+ {
+ $returnam[$ty]=trim($line_end);
+ }
+ return $returnam;
+ break;
+ case 'FN':
+ $exploded = explode (';',$marker);
+ $exploded2 = explode (';',$line_end);
+ $exploded2= array_filter($exploded2);
+ if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded)){
+ $returnam = trim(quoted_printable_decode($exploded2[0]));
+ }
+ else{
+
+ $returnam = trim($exploded2[0]);
+ }
+
+ return $returnam;
+ break;
+ case 'PHOTO':
+ return '';
+ break;
+ case 'NOTE':
+ return '';
+ break;
+ default:
+ break;
+ }
+
+ return ;
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/test.vcf b/test.vcf
new file mode 100644
index 00000000..472bb8ef
--- /dev/null
+++ b/test.vcf
@@ -0,0 +1,8 @@
+BEGIN:VCARD
+VERSION:3.0
+N:Shagnasty;Bolivar;Odysseus;Mr.;III,B.S.
+FN:Bolivar Shagnasty
+ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
+EMAIL;TYPE=HOME;TYPE=WORK:boshag@example.com
+EMAIL;TYPE=PREF:boshag@ciaweb.net
+END:VCARD
From 262802583db0a3eecc13b3d7f1f83356fde1d939 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Wed, 11 May 2011 00:36:33 +0300
Subject: [PATCH 04/44] rm'd temp file
---
parser_class2.php | 125 ----------------------------------------------
1 file changed, 125 deletions(-)
delete mode 100644 parser_class2.php
diff --git a/parser_class2.php b/parser_class2.php
deleted file mode 100644
index f721b038..00000000
--- a/parser_class2.php
+++ /dev/null
@@ -1,125 +0,0 @@
-
-';
-print_r($parse->vCards);
-echo '';
-
- /**
- *
- * Class parses vCard file and returns array with information form vCard.
- *
- * @access public
- *
- * @return array with vCards
- *
- */
-class vCard{
-
- public $mode; //single, multiple, error
- public $vCards = array();
-
- /**
- *
- * Functions which reads text line by line from file
- * and puts it into formatted array
- *
- * @access public
- *
- * @return array with one or multiple vCards()
- *
- */
- public function vCard($path){
- $lines = file($path);
- $card = array();
- //$counter = 0;
-
- foreach($lines as $line){
-
- $pos=strpos($line, 'BEGIN:VCARD');
- if($pos !== false){
- $vCard = array();
- continue;
- }
-
- $pos = strpos($line,':');
-
- if($pos !== false){
- $pos2 = strpos($line,'END:VCARD');
- if($pos2 !== false){
- $this->vCards[] = $vCard;
- continue;
- }
- $marker=$this->escape(substr($line,0,$pos));
- $line_end=$this->escape(substr($line,$pos+1,strlen($line)));
- $pos3=strpos($marker,';');
-
- if(strpos($marker,';')!==false){
- $a=$this->Qcode($marker, $line_end);
- $vCard[$a[0]]=$a[1];
- }
- else{
- $vCard[$marker] = $line_end;
- }
-
- }else{
- $a=$this->Qcode($marker, $line_end);
- $vCard[$a[0]] = $vCard[$a[0]] . $this->escape($line);
-
- }
- }
- }
- /**
- *
- * Function checks if in string is any formatting rule, and
- * if there is any then returns cleared marker and formatted string
- * @access public
- *
- * @return cleared text string
- * @return formated marker
- *
- * @see vCard()
- *
- */
- public function Qcode($marker,$line_end){
- $exploded = explode (';',$marker);
- if (in_array('QUOTED-PRINTABLE',$exploded)){
- $returnam = array(substr($marker,0,strpos($marker,';')),trim(quoted_printable_decode($line_end)));
- return $returnam;
- }
- else{
- $returnam = array(substr($marker,0,strpos($marker,';')),trim($line_end));
- return $returnam;
- }
-
- }
-
-
- /**
- *
- * Functions which clears text from unneccessary symbols
- *
- * @access public
- *
- * @return cleared text string
- *
- * @see vCard()
- *
- */
-
- public function escape(&$text){
- $text = str_replace('\:', ':', $text);
- $text = str_replace('\;', ';', $text);
- $text = str_replace(';;', '', $text);
- $text = str_replace('\,', ',', $text);
- $text = str_replace("\n", "", $text);
-
-
- return $text;
- }
-}
-
-?>
\ No newline at end of file
From 6aa5a55a46817990659324f1f24d6b91a08a8250 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Wed, 11 May 2011 09:48:28 +0300
Subject: [PATCH 05/44] Reworked parser class
---
parser_class.php | 577 +++++++++++++++++++++++------------------------
test.php | 35 ++-
2 files changed, 318 insertions(+), 294 deletions(-)
diff --git a/parser_class.php b/parser_class.php
index 1caf888f..9b381c55 100644
--- a/parser_class.php
+++ b/parser_class.php
@@ -1,327 +1,326 @@
-
';
-print_r($parse->vCards);
-
-
-echo '';
-
- /**
- *
- * Class parses vCard file and returns array with information form vCard.
- *
- * @access public
- *
- * @return array with vCards
- *
- */
-class vCard{
-
- public $mode; //single, multiple, error
- public $vCards = array();
- public $marker0;
- /**
- *
- * Functions which reads text line by line from file
- * and puts it into formatted array
- *
- * @access public
- *
- * @return array with one or multiple vCards()
- *
- */
- public function __construct($path){
- $lines = file($path);
- $card = array();
- $remember='';
- foreach($lines as $line){
-
- $pos = strpos($line,':');
- $end=strlen(trim($line));
- if(substr(trim($line),$end-1, $end)=='='){
- $remember=$line;
- continue;
- }else{
- $remember='';
- }
- if($remember=''){
-
- //if it is beginning of card - skip
- $pos=strpos($line, 'BEGIN:VCARD');
- if($pos !== false){
- $vCard = array();
- continue;
- }
-
- }else{
-
- $line=$line.$remember;
- $remember='';
-
- //if it is end of card - skip
- if($pos !== false){
- $pos2 = strpos($line,'END:VCARD');
- if($pos2 !== false){
- $this->vCards[] = $vCard;
- $vCard = array();
- continue;
+ /**
+ * @var string Current object mode - error, single or multiple (for a single vCard within a file and multiple combined vCards)
+ */
+ private $Mode; //single, multiple, error
+
+ private $Path = '';
+ private $RawData = '';
+
+ /**
+ * @var array Internal data container. Contains vCard objects for multiple vCards and just the data for single vCards.
+ */
+ private $Data = array();
+
+ /**
+ * @static Parts of structured elements according to the spec.
+ */
+ private static $Spec_StructuredElements = array(
+ 'n' => array('LastName', 'FirstName', 'AdditionalNames', 'Prefixes', 'Suffixes'),
+ 'adr' => array('POBox', 'ExtendedAddress', 'StreetAddress', 'Locality', 'Region', 'PostalCode', 'Country'),
+ 'geo' => array('Latitude', 'Longitude'),
+ 'org' => array('Name', 'Unit1', 'Unit2')
+ );
+ private static $Spec_MultipleValueElements = array('nickname', 'categories');
+
+ /**
+ * vCard constructor
+ *
+ * @param string Path to file, optional.
+ * @param string Raw data, optional.
+ *
+ * One of these parameters must be provided, otherwise an exception is thrown.
+ */
+ public function __construct($Path = false, $RawData = false)
+ {
+ // Checking preconditions for the parser.
+ // If path is given, the file should be accessible.
+ // If raw data is given, it is taken as it is.
+ // In both cases the real content is put in $this -> RawData
+ if ($Path)
+ {
+ if (!is_readable($Path))
+ {
+ throw new Exception('vCard: Path not accessible ('.$Path.')');
+ }
+
+ $this -> Path = $Path;
+ $this -> RawData = file_get_contents($this -> Path);
}
-
- //clear marker and line
-
- $marker=$this->escape(substr($line,0,$pos));
- $this->marker0 = $marker;
- $line_end=$this->escape(substr($line,$pos+1,strlen($line)));
- $pos3=strpos($marker,';');
-
-
- //if there are arguments like N;Type=Work: John Smith
- if(strpos($marker,';')!==false){
- $newvar=$this->split($marker,$line_end);
- $this->insert($newvar,$marker,$pos3,$vCard);
-
+ elseif ($RawData)
+ {
+ $this -> RawData = $RawData;
}
- else// if there are no arguments like FN:John Smith
+ else
{
- $newvar=$this->split($marker,$line_end);
- $this->insert($newvar,$marker,strlen($marker),$vCard);
-
+ throw new Exception('vCard: No content provided');
}
- }
- }
- }
- }
-
-
- /**
- *
- * Functions which inserts data into array
- *
- *
- * @access public
- *
- * @return nothing
- *
- */
-
-public function insert($newvar,$marker,$pos3,&$vCard){
-
- if(is_array($newvar)){
- while (list($key, $value) = each($newvar)) {
-
- if(!isset($vCard[substr($marker,0,$pos3)])){
- $vCard[substr($marker,0,$pos3)][$key]=$value;
-
- }
-
- else{
-
- if(is_array($vCard[substr($marker,0,$pos3)]))//if already tehre are such kid info
- {
- if(isset($vCard[substr($marker,0,$pos3)][$key])){
- if(count($vCard[substr($marker,0,$pos3)][$key])==1)
+
+ // Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
+ // If there is only one, this is a single vCard, if more, multiple vCards are combined.
+ $vCardBeginCount = substr_count($this -> RawData, 'BEGIN:VCARD');
+ $vCardEndCount = substr_count($this -> RawData, 'END:VCARD');
+
+ if (($vCardBeginCount != $vCardEndCount) || !$vCardBeginCount)
+ {
+ $this -> Mode = vCard::MODE_ERROR;
+ throw new Exception('vCard: invalid vCard');
+ }
+
+ $this -> Mode = $vCardBeginCount == 1 ? vCard::MODE_SINGLE : vCard::MODE_MULTIPLE;
+
+ // Removing/changing inappropriate newlines, i.e., all CRs or multiple newlines are changed to a single newline
+ $this -> RawData = str_replace("\r", "\n", $this -> RawData);
+ $this -> RawData = preg_replace('{(\n+)}', "\n", $this -> RawData);
+
+ // In multiple card mode the raw text is split at card beginning markers and each
+ // fragment is parsed in a separate vCard object.
+ if ($this -> Mode == self::MODE_MULTIPLE)
+ {
+ $this -> RawData = explode('BEGIN:VCARD', $this -> RawData);
+ $this -> RawData = array_filter($this -> RawData);
+
+ foreach ($this -> RawData as $SinglevCardRawData)
+ {
+ // Prepending "BEGIN:VCARD" to the raw string because we exploded on that one.
+ // If there won't be the BEGIN marker in the new object, it will fail.
+
+ $SinglevCardRawData = 'BEGIN:VCARD'."\n".$SinglevCardRawData;
+ $this -> Data[] = new vCard(false, $SinglevCardRawData);
+ }
+ }
+ else
+ {
+ // Joining multiple lines that are split with a hard wrap and indicated by an equals sign at the end of line
+ $this -> RawData = str_replace("=\n", '', $this -> RawData);
+
+ // Joining multiple lines that are split with a soft wrap (space or tab on the beginning of the next line
+ $this -> RawData = str_replace(array("\n ", "\n\t"), '', $this -> RawData);
+
+ $Lines = explode("\n", $this -> RawData);
+
+ foreach ($Lines as $Line)
+ {
+ // Lines without colons are skipped because, most likely, they contain no data.
+ if (strpos($Line, ':') === false)
+ {
+ continue;
+ }
+
+ // Each line is split into two parts. The key contains the element name and additional parameters, if present,
+ // value is just the value
+ list($Key, $Value) = explode(':', $Line, 2);
+
+ // Key is transformed to lowercase because, even though the element and parameter names are written in uppercase,
+ // it is quite possible that they will be in lower- or mixed case.
+ $Key = strtolower(trim(self::Unescape($Key)));
+
+ // These two lines can be skipped as they aren't necessary at all.
+ if ($Key == 'begin' || $Key == 'end')
+ {
+ continue;
+ }
+
+ $Value = trim(self::Unescape($Value));
+ $Type = array();
+
+ // Here additional parameters are parsed
+ $KeyParts = explode(';', $Key);
+ $Key = $KeyParts[0];
+
+ if (count($KeyParts) > 1)
+ {
+ // Parameters are split into (key, value) pairs
+ $Parameters = array_map(function($Item)
+ {
+ return explode('=', strtolower($Item));
+ },
+ array_slice($KeyParts, 1));
+
+ // And each parameter is checked whether anything can/should be done because of it
+ foreach ($Parameters as $Parameter)
+ {
+ if (count($Parameter) != 2)
+ {
+ continue;
+ }
+
+ if ($Parameter[0] == 'encoding')
+ {
+ if ($Parameter[1] == 'quoted-printable')
{
- $mas=$vCard[substr($marker,0,$pos3)][$key];
- $vCard[substr($marker,0,$pos3)][$key]=array('1'=>$value);
- $vCard[substr($marker,0,$pos3)][$key][]=$mas;
+ $Value = quoted_printable_decode($Value);
}
- else{
- $vCard[substr($marker,0,$pos3)][$key][]=$value;
+ }
+ elseif ($Parameter[0] == 'charset')
+ {
+ if ($Parameter[1] != 'utf-8' && $Parameter[1] != 'utf8')
+ {
+ $Value = mb_convert_encoding($Value, 'UTF-8', $Parameter[1]);
}
}
- else
+ elseif ($Parameter[0] == 'type')
{
- $vCard[substr($marker,0,$pos3)][$key]=$value;
+ $Type = explode(',', $Parameter[1]);
}
+ }
+ }
-
- }
- else{
- $perms= $vCard[substr($marker,0,$pos3)];
- $vCard[substr($marker,0,$pos3)][$key]=$value;
- $vCard[substr($marker,0,$pos3)][]=$perms;
-
+ // Values are parsed according to their type
+ if (isset(self::$Spec_StructuredElements[$Key]))
+ {
+ $Value = self::ParseStructuredValue($Value, $Key);
+ if ($Type)
+ {
+ $Value['Type'] = $Type;
}
-
- }
-
- }
- }
- else
- {
- if($newvar!=''){
- $marker=substr($marker,0, $pos3);
- $vCard[$marker]=$newvar;
- }
+ }
+ elseif (in_array($Key, self::$Spec_MultipleValueElements))
+ {
+ $Value = self::ParseMultipleTextValue($Value, $Key);
+ if ($Type)
+ {
+ $Value = array(
+ 'Value' => $Value,
+ 'Type' => $Type
+ );
+ }
+ }
+ else
+ {
+ if ($Type)
+ {
+ $Value = array(
+ 'Value' => $Value,
+ 'Type' => $Type
+ );
+ }
+ }
+
+ if (!isset($this -> Data[$Key]))
+ {
+ $this -> Data[$Key] = array();
+ }
+
+ $this -> Data[$Key][] = $Value;
+ }
}
-
- return;
-}
-
- /**
- *
- * Functions which clears text from unneccessary symbols
- *
- * @access public
- *
- * @return cleared text string
- *
- * @see vCard()
- *
- */
-
- public function escape(&$text){
- $text = str_replace('\:', ':', $text);
- $text = str_replace('\;', ';', $text);
- $text = str_replace(';;', ';', $text);
- $text = str_replace('\,', ',', $text);
- $text = str_replace("\n", "", $text);
- return $text;
- }
-
- /**
- *
- * Functions which escape text from last symbols
- *
- * @access public
- *
- * @return string with text
- *
- */
- public function finalescape(&$text){
- $text = str_replace(";", "", $text);
- $text = str_replace(";;", "", $text);
- return $text;
- }
-
- /**
- *
- * Functions which splits name and address into
- * multiple fields
- *
- * @access public
- *
- * @return array with values
- *
- */
-
-
- public function split($marker,$line_end)
- {
- $pos=strpos($marker,';');
- if($pos!==false){
- $marker2=substr($marker,0, $pos);
- }
- else{
- $marker2=$marker;
- }
-
- switch ($marker2) {
- case 'N'://N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Dombrovskis;J=C4=81nis;;;
- $exploded = explode (';',$marker);
- $exploded2 = explode (';',$line_end);
- $exploded2= array_filter($exploded2);
- if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded)){
- $returnam = array('LastName'=>trim(quoted_printable_decode($exploded2[0])),'Name'=>trim(quoted_printable_decode($exploded2[1])));
}
- else{
- $returnam = array('LastName'=>trim($exploded2[0]),'Name'=>trim($exploded2[1]));
+
+ /**
+ * Magic method to get the various vCard values as object members, e.g.
+ * a call to $vCard -> N gets the "N" value
+ *
+ * @param string Key
+ *
+ * @return mixed Value
+ */
+ public function __get($Key)
+ {
+ if (isset($this -> Data[$Key]))
+ {
+ return $this -> Data[$Key];
+ }
+ elseif ($Key == 'Mode')
+ {
+ return $this -> Mode;
+ }
+ return null;
}
- return $returnam;
- break;
- case 'ADR'://ADR;TYPE=HOME:;;Eksporta 12-256;Riga;;;Latvia
+ // !Helper methods
- $line_end =substr($line_end,1,strlen($line_end));
- $line_end = str_replace(';', ' ', $line_end);
- $exploded2= explode(';',$marker);
- if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded2)){
- $returnam = trim(quoted_printable_decode($line_end));
- }
- else{
- $returnam = trim($line_end);
+ /**
+ * Removes the escaping slashes from the text.
+ *
+ * @access private
+ *
+ * @param string Text to prepare.
+ *
+ * @return string Resulting text.
+ */
+ private static function Unescape($Text)
+ {
+ return str_replace(array('\:', '\;', ';', '\,', "\n"), array(':', ';', ';', ',', ''), $Text);
}
- return $returnam;
- break;
- case 'URL':
- $types=substr($this->marker0,9,trim(strlen($this->marker0)));
- $types=explode(',',$types);
- foreach($types as $ty)
+ /**
+ * Separates the various parts of a structured value according to the spec.
+ *
+ * @access private
+ *
+ * @param string Raw text string
+ * @param string Key (e.g., N, ADR, ORG, etc.)
+ *
+ * @return array Parts in an associative array.
+ */
+ private static function ParseStructuredValue($Text, $Key)
{
- $returnam[$ty]=trim($line_end);
+ $Text = array_map('trim', explode(';', $Text));
+
+ $Result = array();
+ for ($i = 0; $i < count($Text) && $i < count(self::$Spec_StructuredElements[$Key]); $i++)
+ {
+ $Result[self::$Spec_StructuredElements[$Key][$i]] = $Text[$i];
+ }
+ return $Result;
}
- return $returnam;
- case 'TEL'://TEL;TYPE=WORK:+371 29594419
- $types=substr($this->marker0,9,trim(strlen($this->marker0)));
- $types=explode(',',$types);
- foreach($types as $ty)
+
+ /**
+ * @access private
+ */
+ private static function ParseMultipleTextValue($Text)
{
- $returnam[$ty]=trim($line_end);
+ return explode(',', $Text);
}
-
- return $returnam;
- break;
- case 'EMAIL'://EMAIL;TYPE=WORK,INTERNET:martins@sales.lv
- $types=substr($this->marker0,11,trim(strlen($this->marker0)));
- $types=explode(',',$types);
- foreach($types as $ty)
+
+ // !Interface methods
+
+ // Countable interface
+ public function count()
{
- $returnam[$ty]=trim($line_end);
+ return count($this -> Data);
}
- return $returnam;
- break;
- case 'X-TWITTER':
- $types=substr($this->marker0,15,trim(strlen($this->marker0)));
- $types=explode(',',$types);
- foreach($types as $ty)
+ // Iterator interface
+ public function rewind()
{
- $returnam[$ty]=trim($line_end);
+ reset($this -> Data);
}
- return $returnam;
- break;
- case 'X-SKYPE':
- $types=substr($this->marker0,13,trim(strlen($this->marker0)));
- $types=explode(',',$types);
- foreach($types as $ty)
+
+ public function current()
{
- $returnam[$ty]=trim($line_end);
+ return current($this -> Data);
}
- return $returnam;
- break;
- case 'FN':
- $exploded = explode (';',$marker);
- $exploded2 = explode (';',$line_end);
- $exploded2= array_filter($exploded2);
- if (in_array('ENCODING=QUOTED-PRINTABLE',$exploded)){
- $returnam = trim(quoted_printable_decode($exploded2[0]));
+
+ public function next()
+ {
+ return next($this -> Data);
}
- else{
-
- $returnam = trim($exploded2[0]);
+
+ public function valid()
+ {
+ return ($this -> current() !== false);
}
- return $returnam;
- break;
- case 'PHOTO':
- return '';
- break;
- case 'NOTE':
- return '';
- break;
- default:
- break;
+ public function key()
+ {
+ return key($this -> Data);
+ }
}
-
- return ;
- }
-
-}
-
?>
\ No newline at end of file
diff --git a/test.php b/test.php
index dc1dead3..428ed634 100644
--- a/test.php
+++ b/test.php
@@ -1,8 +1,33 @@
';
+ print_r($vCard -> N);
+ print_r($vCard -> EMAIL);
+ echo '';
+ }
+ // if the file contains multiple vCards, they are accessible as elements of an array
+ else
+ {
+ foreach ($vCard as $Index => $vCardPart)
+ {
+ echo '';
+ print_r($vCardPart -> n);
+ print_r($vCardPart -> tel);
+ print_r($vCardPart -> email);
+ print_r($vCardPart -> phone);
+ print_r($vCardPart -> adr);
+ echo '
';
+ echo '
';
+ }
+ }
?>
\ No newline at end of file
From 92edbb9b90ab16bd0ef0f359169f272d18907674 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Wed, 11 May 2011 14:56:16 +0300
Subject: [PATCH 06/44] Minor cleanup
---
parser_class.php | 17 +++++------------
test.vcf | 9 +++++++++
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/parser_class.php b/parser_class.php
index 9b381c55..1571f75a 100644
--- a/parser_class.php
+++ b/parser_class.php
@@ -189,20 +189,13 @@ public function __construct($Path = false, $RawData = false)
$Value['Type'] = $Type;
}
}
- elseif (in_array($Key, self::$Spec_MultipleValueElements))
+ else
{
- $Value = self::ParseMultipleTextValue($Value, $Key);
-
- if ($Type)
+ if (in_array($Key, self::$Spec_MultipleValueElements))
{
- $Value = array(
- 'Value' => $Value,
- 'Type' => $Type
- );
+ $Value = self::ParseMultipleTextValue($Value, $Key);
}
- }
- else
- {
+
if ($Type)
{
$Value = array(
@@ -256,7 +249,7 @@ public function __get($Key)
*/
private static function Unescape($Text)
{
- return str_replace(array('\:', '\;', ';', '\,', "\n"), array(':', ';', ';', ',', ''), $Text);
+ return str_replace(array('\:', '\;', '\,', "\n"), array(':', ';', ',', ''), $Text);
}
/**
diff --git a/test.vcf b/test.vcf
index 472bb8ef..7fad73f6 100644
--- a/test.vcf
+++ b/test.vcf
@@ -1,5 +1,14 @@
BEGIN:VCARD
VERSION:3.0
+N:John;Doe;W;Mr.;III,B.S.
+FN:John
+ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
+EMAIL;TYPE=HOME;TYPE=WORK:test@example.com
+EMAIL;TYPE=HOME;TYPE=WORK:test2@example.com
+EMAIL;TYPE=PREF:boshag@ciaweb.net
+END:VCARD
+BEGIN:VCARD
+VERSION:3.0
N:Shagnasty;Bolivar;Odysseus;Mr.;III,B.S.
FN:Bolivar Shagnasty
ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
From 49823d0b64446080f775d0b7263a489a2999eaf8 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Sat, 14 May 2011 12:57:58 +0300
Subject: [PATCH 07/44] Renamed the class file
---
parser_class.php => vCard.php | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename parser_class.php => vCard.php (100%)
diff --git a/parser_class.php b/vCard.php
similarity index 100%
rename from parser_class.php
rename to vCard.php
From 94fdc9b7fbd78385d28a604c31b35bf080f38b80 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Sat, 14 May 2011 13:22:58 +0300
Subject: [PATCH 08/44] Cleanup, README, new example files
---
Example2.1.vcf | 15 +++++++++++++++
Example3.0.vcf | 16 ++++++++++++++++
README | 39 +++++++++++++++++++++++++++++++++++++++
test.php | 8 ++++----
test.vcf | 17 -----------------
vCard.php | 14 +++++++++++++-
6 files changed, 87 insertions(+), 22 deletions(-)
create mode 100644 Example2.1.vcf
create mode 100644 Example3.0.vcf
delete mode 100644 test.vcf
diff --git a/Example2.1.vcf b/Example2.1.vcf
new file mode 100644
index 00000000..5e88420a
--- /dev/null
+++ b/Example2.1.vcf
@@ -0,0 +1,15 @@
+BEGIN:VCARD
+VERSION:2.1
+N:Gump;Forrest
+FN:Forrest Gump
+ORG:Bubba Gump Shrimp Co.
+TITLE:Shrimp Man
+TEL;WORK;VOICE:(111) 555-1212
+TEL;HOME;VOICE:(404) 555-1212
+ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
+LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America
+ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
+LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, LA 30314=0D=0AUnited States of America
+EMAIL;PREF;INTERNET:forrestgump@example.com
+REV:20080424T195243Z
+END:VCARD
\ No newline at end of file
diff --git a/Example3.0.vcf b/Example3.0.vcf
new file mode 100644
index 00000000..ec66d68b
--- /dev/null
+++ b/Example3.0.vcf
@@ -0,0 +1,16 @@
+BEGIN:VCARD
+VERSION:3.0
+N:Gump;Forrest
+FN:Forrest Gump
+ORG:Bubba Gump Shrimp Co.
+TITLE:Shrimp Man
+PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
+TEL;TYPE=WORK,VOICE:(111) 555-1212
+TEL;TYPE=HOME,VOICE:(404) 555-1212
+ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
+LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
+ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
+LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
+EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
+REV:20080424T195243Z
+END:VCARD
\ No newline at end of file
diff --git a/README b/README
index e69de29b..62c70b51 100644
--- a/README
+++ b/README
@@ -0,0 +1,39 @@
+Nuovo's vCard-parser is a simple vCard file parser with the focus on ease of use.
+
+The parser was written mostly because I couldn't find one that I was satisfied with - all those that I tried either failed with real world data or were too unwieldy or inconvenient, hence this parser.
+
+The parser can read both single and multiple vCards from a single file and with the help of PHP's magic methods and interfaces it can be written concisely. For example:
+
+$vCard = new vCard('Example3.0.vcf');
+
+// Get the number of vCards in the file
+echo count($vCard);
+
+// In the single-vCard mode every element is accessible directly.
+if (count($vCard) == 1)
+{
+ print_r($vCard -> n);
+ print_r($vCard -> tel);
+}
+// In the multiple-vCard mode the object can be used as an array to retrieve separate vCard objects
+// for each vCard in the file.
+else
+{
+ foreach ($vCard as $vCardPart)
+ {
+ print_r($vCardPart -> n);
+ print_r($vCardPart -> tel);
+ }
+}
+
+Every vCard element is accessible as an object member by the vCard element name. Every element is an array with the data parsed out of the file.
+
+See also:
+- http://tools.ietf.org/html/rfc2425
+- http://tools.ietf.org/html/rfc2426
+
+TODOs planned:
+- Add support for non-standard ("X-...") elements;
+- Easier data access without the necessity to traverse multiple arrays.
+
+http://www.nuovo.lv
\ No newline at end of file
diff --git a/test.php b/test.php
index 428ed634..82fc3765 100644
--- a/test.php
+++ b/test.php
@@ -1,7 +1,7 @@
';
- print_r($vCard -> N);
- print_r($vCard -> EMAIL);
+ print_r($vCard -> n);
+ print_r($vCard -> tel);
echo '';
}
// if the file contains multiple vCards, they are accessible as elements of an array
diff --git a/test.vcf b/test.vcf
deleted file mode 100644
index 7fad73f6..00000000
--- a/test.vcf
+++ /dev/null
@@ -1,17 +0,0 @@
-BEGIN:VCARD
-VERSION:3.0
-N:John;Doe;W;Mr.;III,B.S.
-FN:John
-ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
-EMAIL;TYPE=HOME;TYPE=WORK:test@example.com
-EMAIL;TYPE=HOME;TYPE=WORK:test2@example.com
-EMAIL;TYPE=PREF:boshag@ciaweb.net
-END:VCARD
-BEGIN:VCARD
-VERSION:3.0
-N:Shagnasty;Bolivar;Odysseus;Mr.;III,B.S.
-FN:Bolivar Shagnasty
-ADR;TYPE=HOME,WORK:;;123 Main,Apartment 101;Beverly Hills;CA;90210
-EMAIL;TYPE=HOME;TYPE=WORK:boshag@example.com
-EMAIL;TYPE=PREF:boshag@ciaweb.net
-END:VCARD
diff --git a/vCard.php b/vCard.php
index 1571f75a..e9d40b51 100644
--- a/vCard.php
+++ b/vCard.php
@@ -287,7 +287,19 @@ private static function ParseMultipleTextValue($Text)
// Countable interface
public function count()
{
- return count($this -> Data);
+ switch ($this -> Mode)
+ {
+ case self::MODE_ERROR:
+ return 0;
+ break;
+ case self::MODE_SINGLE:
+ return 1;
+ break;
+ case self::MODE_MULTIPLE:
+ return count($this -> Data);
+ break;
+ }
+ return 0;
}
// Iterator interface
From e7535b6267fe83c48f716aac8c3f0045fd08be45 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Sat, 14 May 2011 13:25:21 +0300
Subject: [PATCH 09/44] Cleanup, README, new example files
---
README | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README b/README
index 62c70b51..933e9517 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-Nuovo's vCard-parser is a simple vCard file parser with the focus on ease of use.
+Nuovo/Nouveau vCard-parser is a simple vCard file parser with the focus on ease of use.
The parser was written mostly because I couldn't find one that I was satisfied with - all those that I tried either failed with real world data or were too unwieldy or inconvenient, hence this parser.
From b1b5d724fb8dd370c3f673116777befeb5cab502 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Sun, 15 May 2011 13:28:41 +0300
Subject: [PATCH 10/44] more README
---
README | 1 +
1 file changed, 1 insertion(+)
diff --git a/README b/README
index 933e9517..aacdd1d0 100644
--- a/README
+++ b/README
@@ -34,6 +34,7 @@ See also:
TODOs planned:
- Add support for non-standard ("X-...") elements;
+- AGENT element should return another vCard object
- Easier data access without the necessity to traverse multiple arrays.
http://www.nuovo.lv
\ No newline at end of file
From c20714e2589c5a8a3114ddc31bb4355e04ed649c Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Sun, 15 May 2011 18:53:41 +0300
Subject: [PATCH 11/44] Improved TYPE parsing, now both TYPE=home,work and
TYPE=home,TYPE=work should work, as well as types without the TYPE=, thus 2.1
types are now working as well.
---
Example3.0.vcf | 1 +
vCard.php | 124 +++++++++++++++++++++++++++++++++++++------------
2 files changed, 96 insertions(+), 29 deletions(-)
diff --git a/Example3.0.vcf b/Example3.0.vcf
index ec66d68b..dd109b52 100644
--- a/Example3.0.vcf
+++ b/Example3.0.vcf
@@ -7,6 +7,7 @@ TITLE:Shrimp Man
PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
TEL;TYPE=WORK,VOICE:(111) 555-1212
TEL;TYPE=HOME,VOICE:(404) 555-1212
+TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
diff --git a/vCard.php b/vCard.php
index e9d40b51..5265a015 100644
--- a/vCard.php
+++ b/vCard.php
@@ -37,6 +37,13 @@ class vCard implements Countable, Iterator
);
private static $Spec_MultipleValueElements = array('nickname', 'categories');
+ private static $Spec_ElementTypes = array(
+ 'email' => array('internet', 'x400', 'pref'),
+ 'adr' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
+ 'label' => array('dom', 'intl', 'postal', 'parcel', 'home', 'work', 'pref'),
+ 'tel' => array('home', 'msg', 'work', 'pref', 'voice', 'fax', 'cell', 'video', 'pager', 'bbs', 'modem', 'car', 'isdn', 'pcs')
+ );
+
/**
* vCard constructor
*
@@ -144,38 +151,27 @@ public function __construct($Path = false, $RawData = false)
if (count($KeyParts) > 1)
{
- // Parameters are split into (key, value) pairs
- $Parameters = array_map(function($Item)
- {
- return explode('=', strtolower($Item));
- },
- array_slice($KeyParts, 1));
+ $Parameters = self::ParseParameters($Key, array_slice($KeyParts, 1));
- // And each parameter is checked whether anything can/should be done because of it
- foreach ($Parameters as $Parameter)
+ foreach ($Parameters as $ParamKey => $ParamValue)
{
- if (count($Parameter) != 2)
+ switch ($ParamKey)
{
- continue;
- }
-
- if ($Parameter[0] == 'encoding')
- {
- if ($Parameter[1] == 'quoted-printable')
- {
- $Value = quoted_printable_decode($Value);
- }
- }
- elseif ($Parameter[0] == 'charset')
- {
- if ($Parameter[1] != 'utf-8' && $Parameter[1] != 'utf8')
- {
- $Value = mb_convert_encoding($Value, 'UTF-8', $Parameter[1]);
- }
- }
- elseif ($Parameter[0] == 'type')
- {
- $Type = explode(',', $Parameter[1]);
+ case 'encoding':
+ if ($Parameter[1] == 'quoted-printable')
+ {
+ $Value = quoted_printable_decode($Value);
+ }
+ break;
+ case 'charset':
+ if ($Parameter[1] != 'utf-8' && $Parameter[1] != 'utf8')
+ {
+ $Value = mb_convert_encoding($Value, 'UTF-8', $Parameter[1]);
+ }
+ break;
+ case 'type':
+ $Type = $ParamValue;
+ break;
}
}
}
@@ -282,6 +278,76 @@ private static function ParseMultipleTextValue($Text)
return explode(',', $Text);
}
+ /**
+ * @access private
+ */
+ private static function ParseParameters($Key, array $RawParams = null)
+ {
+ if (!$RawParams)
+ {
+ return array();
+ }
+
+ // Parameters are split into (key, value) pairs
+ $Parameters = array_map(function($Item)
+ {
+ return explode('=', strtolower($Item));
+ },
+ $RawParams);
+
+ $Type = array();
+ $Result = array();
+
+ // And each parameter is checked whether anything can/should be done because of it
+ foreach ($Parameters as $Index => $Parameter)
+ {
+ // Skipping empty elements
+ if (!$Parameter)
+ {
+ continue;
+ }
+
+ // Handling type parameters without the explicit TYPE parameter name (2.1 valid)
+ if (count($Parameter) == 1)
+ {
+ if (isset(self::$Spec_ElementTypes[$Key]) && in_array($Parameter, self::$Spec_ElementTypes[$Key]))
+ {
+ $Type[] = $Parameter;
+ }
+ }
+ elseif (count($Parameter) > 2)
+ {
+ $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index]));
+ if ($TempTypeParams['type'])
+ {
+ $Type = array_merge($Type, $TempTypeParams['type']);
+ }
+ }
+ else
+ {
+ if ($Parameter[0] == 'encoding')
+ {
+ if ($Parameter[1] == 'quoted-printable')
+ {
+ $Result['encoding'] = 'quoted-printable';
+ }
+ }
+ elseif ($Parameter[0] == 'charset')
+ {
+ $Result['charset'] = $Parameter[1];
+ }
+ elseif ($Parameter[0] == 'type')
+ {
+ $Type = array_merge($Type, explode(',', $Parameter[1]));
+ }
+ }
+ }
+
+ $Result['type'] = $Type;
+
+ return $Result;
+ }
+
// !Interface methods
// Countable interface
From e762de673934567fad7cab27526bf8358f0a7202 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C4=81rti=C5=86=C5=A1=20Pils=C4=93tnieks?=
Date: Fri, 25 Nov 2011 13:00:59 +0200
Subject: [PATCH 12/44] Experimental support for writing vCards
---
README | 5 +-
test.php | 1 +
vCard.php | 145 ++++++++++++++++++++++++++++++++++++++++++++++++-
write-test.php | 13 +++++
4 files changed, 159 insertions(+), 5 deletions(-)
create mode 100644 write-test.php
diff --git a/README b/README
index aacdd1d0..8d37260f 100644
--- a/README
+++ b/README
@@ -34,7 +34,8 @@ See also:
TODOs planned:
- Add support for non-standard ("X-...") elements;
-- AGENT element should return another vCard object
-- Easier data access without the necessity to traverse multiple arrays.
+- AGENT element should return another vCard object;
+- Easier data access without the necessity to traverse multiple arrays;
+- Logos, photos, sounds should be made workable.
http://www.nuovo.lv
\ No newline at end of file
diff --git a/test.php b/test.php
index 82fc3765..40661210 100644
--- a/test.php
+++ b/test.php
@@ -13,6 +13,7 @@
echo '';
print_r($vCard -> n);
print_r($vCard -> tel);
+ print_r($vCard);
echo '
';
}
// if the file contains multiple vCards, they are accessible as elements of an array
diff --git a/vCard.php b/vCard.php
index 5265a015..4757a135 100644
--- a/vCard.php
+++ b/vCard.php
@@ -1,11 +1,11 @@
Path && !$this -> RawData)
+ {
+ return true;
}
// Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
@@ -232,8 +240,139 @@ public function __get($Key)
return null;
}
+ /**
+ * Magic method for adding data to the vCard
+ *
+ * @param string Key
+ * @param string Method call arguments. First element is value.
+ *
+ * @return vCard Current object for method chaining
+ */
+ public function __call($Key, $Arguments)
+ {
+ if (!isset($this -> Data[$Key]))
+ {
+ $this -> Data[$Key] = array();
+ }
+
+ $Value = isset($Arguments[0]) ? $Arguments[0] : false;
+
+ if (!$Value)
+ {
+ return $this;
+ }
+
+ if (count($Arguments) > 1)
+ {
+ $Types = array_values(array_slice($Arguments, 1));
+
+ if (isset(self::$Spec_StructuredElements[strtolower($Key)]) &&
+ in_array($Arguments[1], self::$Spec_StructuredElements[strtolower($Key)])
+ )
+ {
+ $LastElementIndex = 0;
+
+ if (count($this -> Data[$Key]))
+ {
+ $LastElementIndex = count($this -> Data[$Key]) - 1;
+ }
+
+ if (isset($this -> Data[$Key][$LastElementIndex]))
+ {
+ if (empty($this -> Data[$Key][$LastElementIndex][$Types[0]]))
+ {
+ $this -> Data[$Key][$LastElementIndex][$Types[0]] = $Value;
+ }
+ else
+ {
+ $LastElementIndex++;
+ }
+ }
+
+ if (!isset($this -> Data[$Key][$LastElementIndex]))
+ {
+ $this -> Data[$Key][$LastElementIndex] = array(
+ $Types[0] => $Value
+ );
+ }
+ }
+ elseif (in_array($Key, array_keys(self::$Spec_ElementTypes)))
+ {
+ $this -> Data[$Key][] = array(
+ 'Value' => $Value,
+ 'Type' => $Types
+ );
+ }
+ }
+ else
+ {
+ $this -> Data[$Key][] = $Value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Magic method for getting vCard content out
+ *
+ * @return string Raw vCard content
+ */
+ public function __toString()
+ {
+ $Text = 'BEGIN:VCARD'.self::endl;
+ $Text .= 'VERSION:3.0'.self::endl;
+
+ foreach ($this -> Data as $Key => $Values)
+ {
+ $KeyUC = strtoupper($Key);
+
+ if (in_array($KeyUC, array('PHOTO', 'VERSION')))
+ {
+ continue;
+ }
+
+ foreach ($Values as $Index => $Value)
+ {
+ $Text .= $KeyUC;
+ if (is_array($Value) && isset($Value['Type']))
+ {
+ $Text .= ';TYPE='.self::PrepareTypeStrForOutput($Value['Type']);
+ }
+ $Text .= ':';
+
+ if (in_array($Key, array_keys(self::$Spec_StructuredElements)))
+ {
+ $PartArray = array();
+ foreach (self::$Spec_StructuredElements[$Key] as $Part)
+ {
+ $PartArray[] = isset($Value[$Part]) ? $Value[$Part] : '';
+ }
+ $Text .= implode(';', $PartArray);
+ }
+ elseif (is_array($Value) && in_array($Key, array_keys(self::$Spec_ElementTypes)))
+ {
+ $Text .= $Value['Value'];
+ }
+ else
+ {
+ $Text .= $Value;
+ }
+
+ $Text .= self::endl;
+ }
+ }
+
+ $Text .= 'END:VCARD'.self::endl;
+ return $Text;
+ }
+
// !Helper methods
+ private static function PrepareTypeStrForOutput($Type)
+ {
+ return implode(',', array_map('strtoupper', $Type));
+ }
+
/**
* Removes the escaping slashes from the text.
*
diff --git a/write-test.php b/write-test.php
new file mode 100644
index 00000000..7314d08c
--- /dev/null
+++ b/write-test.php
@@ -0,0 +1,13 @@
+ n('John', 'FirstName');
+ $vCard -> n('Doe', 'LastName');
+ $vCard -> tel('555-1111');
+ $vCard -> tel('555-1234', 'Work');
+
+ $vCard = new vCard('Example3.0.vcf');
+
+ echo ''.$vCard.'
';
+?>
\ No newline at end of file
From b64b18258d712c2de5e46c1ac2b06448ab5cb330 Mon Sep 17 00:00:00 2001
From: Martins
Date: Sat, 21 Apr 2012 02:03:56 +0300
Subject: [PATCH 13/44] Operations with files added
Embedded files can now be handled easier - either saved or output.
---
test.php | 25 +++++++++++++++
vCard.php | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 105 insertions(+), 11 deletions(-)
diff --git a/test.php b/test.php
index 40661210..6599f202 100644
--- a/test.php
+++ b/test.php
@@ -15,6 +15,31 @@
print_r($vCard -> tel);
print_r($vCard);
echo '';
+
+ if ($vCard -> photo)
+ {
+ // If there is a photo or a logo, or a sound embedded in the file,
+ // it can be accessed directly, for example:
+ foreach ($vCard -> photo as $Photo)
+ {
+ // You have to take into account that the file can also be a URI (in that case the Encoding parameter will be "uri" instead of "b"
+ if ($Photo['Encoding'] == 'b')
+ {
+ echo '
';
+ }
+ elseif ($Photo['Encoding'] == 'uri')
+ {
+ echo '
';
+ }
+ }
+
+ // It can also be saved to a file
+ $vCard -> SaveFile('photo', 0, 'test_image.jpg');
+ // The parameters are:
+ // - name of the file we want to save (photo, logo or sound)
+ // - index of the file in case of multiple files (defaults to 0)
+ // - target path to save to, including the filenam
+ }
}
// if the file contains multiple vCards, they are accessible as elements of an array
else
diff --git a/vCard.php b/vCard.php
index 4757a135..0e0b5620 100644
--- a/vCard.php
+++ b/vCard.php
@@ -1,12 +1,12 @@
array('home', 'msg', 'work', 'pref', 'voice', 'fax', 'cell', 'video', 'pager', 'bbs', 'modem', 'car', 'isdn', 'pcs')
);
+ private static $Spec_FileElements = array('photo', 'logo', 'sound');
+
/**
* vCard constructor
*
@@ -156,6 +158,7 @@ public function __construct($Path = false, $RawData = false)
// Here additional parameters are parsed
$KeyParts = explode(';', $Key);
$Key = $KeyParts[0];
+ $Encoding = false;
if (count($KeyParts) > 1)
{
@@ -166,7 +169,12 @@ public function __construct($Path = false, $RawData = false)
switch ($ParamKey)
{
case 'encoding':
- if ($Parameter[1] == 'quoted-printable')
+ $Encoding = $ParamValue;
+ if ($ParamValue == 'b')
+ {
+ //$Value = base64_decode($Value);
+ }
+ elseif ($Parameters[1] == 'quoted-printable')
{
$Value = quoted_printable_decode($Value);
}
@@ -209,6 +217,11 @@ public function __construct($Path = false, $RawData = false)
}
}
+ if (is_array($Value) && $Encoding)
+ {
+ $Value['Encoding'] = $Encoding;
+ }
+
if (!isset($this -> Data[$Key]))
{
$this -> Data[$Key] = array();
@@ -231,6 +244,19 @@ public function __get($Key)
{
if (isset($this -> Data[$Key]))
{
+ if (in_array($Key, self::$Spec_FileElements))
+ {
+ $Value = $this -> Data[$Key];
+ foreach ($Value as $K => $V)
+ {
+ if (stripos($V['Value'], 'uri:') === 0)
+ {
+ $Value[$K]['Value'] = substr($V, 4);
+ $Value[$K]['Encoding'] = 'uri';
+ }
+ }
+ return $Value;
+ }
return $this -> Data[$Key];
}
elseif ($Key == 'Mode')
@@ -240,6 +266,49 @@ public function __get($Key)
return null;
}
+ /**
+ * Saves an embedded file
+ *
+ * @param string Key
+ * @param int Index of the file, defaults to 0
+ * @param string Target path where the file should be saved, including the filename
+ *
+ * @return bool Operation status
+ */
+ public function SaveFile($Key, $Index = 0, $TargetPath = '')
+ {
+ if (!isset($this -> Data[$Key]))
+ {
+ return false;
+ }
+ if (!isset($this -> Data[$Key][$Index]))
+ {
+ return false;
+ }
+
+ // Returing false if it is an image URL
+ if (stripos($this -> Data[$Key][$Index]['Value'], 'uri:') === 0)
+ {
+ return false;
+ }
+
+ if (is_writable($TargetPath) || (!file_exists($TargetPath) && is_writable(dirname($TargetPath))))
+ {
+ $RawContent = $this -> Data[$Key][$Index]['Value'];
+ if (isset($this -> Data[$Key][$Index]['Encoding']) && $this -> Data[$Key][$Index]['Encoding'] == 'b')
+ {
+ $RawContent = base64_decode($RawContent);
+ }
+ $Status = file_put_contents($TargetPath, $RawContent);
+ return (bool)$Status;
+ }
+ else
+ {
+ throw new Exception('vCard: Cannot save file ('.$Key.'), target path not writable ('.$TargetPath.')');
+ }
+ return false;
+ }
+
/**
* Magic method for adding data to the vCard
*
@@ -466,9 +535,9 @@ private static function ParseParameters($Key, array $RawParams = null)
{
if ($Parameter[0] == 'encoding')
{
- if ($Parameter[1] == 'quoted-printable')
+ if (in_array($Parameter[1], array('quoted-printable', 'b')))
{
- $Result['encoding'] = 'quoted-printable';
+ $Result['encoding'] = $Parameter[1];
}
}
elseif ($Parameter[0] == 'charset')
From c500f3fc72954d36cd6a7b5fd2d6ae484c765e55 Mon Sep 17 00:00:00 2001
From: Martins
Date: Sat, 21 Apr 2012 03:08:25 +0300
Subject: [PATCH 14/44] Agent vCards
Agent vCards are now supported as well.
---
Example3.0.vcf | 17 ++++++++++++++
README | 3 ---
test.php | 19 +++++++++++-----
vCard.php | 61 +++++++++++++++++++++++++++++++++++---------------
4 files changed, 74 insertions(+), 26 deletions(-)
diff --git a/Example3.0.vcf b/Example3.0.vcf
index dd109b52..0ce82d7c 100644
--- a/Example3.0.vcf
+++ b/Example3.0.vcf
@@ -13,5 +13,22 @@ LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
+AGENT:BEGIN:VCARD
+ VERSION:3.0
+ N:Gump;Forrest
+ FN:Forrest Gump
+ ORG:Bubba Gump Shrimp Co.
+ TITLE:Shrimp Man
+ PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
+ TEL;TYPE=WORK,VOICE:(111) 555-1212
+ TEL;TYPE=HOME,VOICE:(404) 555-1212
+ TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
+ ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
+ LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
+ ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
+ LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
+ EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
+ REV:20080424T195243Z
+ END:VCARD
REV:20080424T195243Z
END:VCARD
\ No newline at end of file
diff --git a/README b/README
index 8d37260f..131e6be1 100644
--- a/README
+++ b/README
@@ -34,8 +34,5 @@ See also:
TODOs planned:
- Add support for non-standard ("X-...") elements;
-- AGENT element should return another vCard object;
-- Easier data access without the necessity to traverse multiple arrays;
-- Logos, photos, sounds should be made workable.
http://www.nuovo.lv
\ No newline at end of file
diff --git a/test.php b/test.php
index 6599f202..07ba13c0 100644
--- a/test.php
+++ b/test.php
@@ -16,6 +16,8 @@
print_r($vCard);
echo '';
+ print_r($vCard -> agent);
+
if ($vCard -> photo)
{
// If there is a photo or a logo, or a sound embedded in the file,
@@ -34,11 +36,18 @@
}
// It can also be saved to a file
- $vCard -> SaveFile('photo', 0, 'test_image.jpg');
- // The parameters are:
- // - name of the file we want to save (photo, logo or sound)
- // - index of the file in case of multiple files (defaults to 0)
- // - target path to save to, including the filenam
+ try
+ {
+ $vCard -> SaveFile('photo', 0, 'test_image.jpg');
+ // The parameters are:
+ // - name of the file we want to save (photo, logo or sound)
+ // - index of the file in case of multiple files (defaults to 0)
+ // - target path to save to, including the filenam
+ }
+ catch (Exception $E)
+ {
+ // Target path not writable
+ }
}
}
// if the file contains multiple vCards, they are accessible as elements of an array
diff --git a/vCard.php b/vCard.php
index 0e0b5620..87ba6ffe 100644
--- a/vCard.php
+++ b/vCard.php
@@ -89,8 +89,8 @@ public function __construct($Path = false, $RawData = false)
// Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
// If there is only one, this is a single vCard, if more, multiple vCards are combined.
- $vCardBeginCount = substr_count($this -> RawData, 'BEGIN:VCARD');
- $vCardEndCount = substr_count($this -> RawData, 'END:VCARD');
+ $vCardBeginCount = preg_match_all('{^BEGIN\:VCARD}miS', $this -> RawData);
+ $vCardEndCount = preg_match_all('{^END\:VCARD}miS', $this -> RawData);
if (($vCardBeginCount != $vCardEndCount) || !$vCardBeginCount)
{
@@ -126,7 +126,7 @@ public function __construct($Path = false, $RawData = false)
$this -> RawData = str_replace("=\n", '', $this -> RawData);
// Joining multiple lines that are split with a soft wrap (space or tab on the beginning of the next line
- $this -> RawData = str_replace(array("\n ", "\n\t"), '', $this -> RawData);
+ $this -> RawData = str_replace(array("\n ", "\n\t"), '-wrap-', $this -> RawData);
$Lines = explode("\n", $this -> RawData);
@@ -152,6 +152,21 @@ public function __construct($Path = false, $RawData = false)
continue;
}
+ if ($Key == 'agent')
+ {
+ $Value = new vCard(false, str_replace('-wrap-', "\n", $Value));
+ if (!isset($this -> Data[$Key]))
+ {
+ $this -> Data[$Key] = array();
+ }
+ $this -> Data[$Key][] = $Value;
+ continue;
+ }
+ else
+ {
+ $Value = str_replace('-wrap-', '', $Value);
+ }
+
$Value = trim(self::Unescape($Value));
$Type = array();
@@ -174,7 +189,7 @@ public function __construct($Path = false, $RawData = false)
{
//$Value = base64_decode($Value);
}
- elseif ($Parameters[1] == 'quoted-printable')
+ elseif ($ParamValue == 'quoted-printable')
{
$Value = quoted_printable_decode($Value);
}
@@ -244,7 +259,11 @@ public function __get($Key)
{
if (isset($this -> Data[$Key]))
{
- if (in_array($Key, self::$Spec_FileElements))
+ if ($Key == 'agent')
+ {
+ return $this -> Data[$Key];
+ }
+ elseif (in_array($Key, self::$Spec_FileElements))
{
$Value = $this -> Data[$Key];
foreach ($Value as $K => $V)
@@ -533,20 +552,26 @@ private static function ParseParameters($Key, array $RawParams = null)
}
else
{
- if ($Parameter[0] == 'encoding')
+ switch ($Parameter[0])
{
- if (in_array($Parameter[1], array('quoted-printable', 'b')))
- {
- $Result['encoding'] = $Parameter[1];
- }
- }
- elseif ($Parameter[0] == 'charset')
- {
- $Result['charset'] = $Parameter[1];
- }
- elseif ($Parameter[0] == 'type')
- {
- $Type = array_merge($Type, explode(',', $Parameter[1]));
+ case 'encoding':
+ if (in_array($Parameter[1], array('quoted-printable', 'b')))
+ {
+ $Result['encoding'] = $Parameter[1];
+ }
+ break;
+ case 'charset':
+ $Result['charset'] = $Parameter[1];
+ break;
+ case 'type':
+ $Type = array_merge($Type, explode(',', $Parameter[1]));
+ break;
+ case 'value':
+ if (strtolower($Parameter[1]) == 'url')
+ {
+ $Result['encoding'] = 'uri';
+ }
+ break;
}
}
}
From fe927ec72ac61109866309ab189a6f3e6ad9dc59 Mon Sep 17 00:00:00 2001
From: Martins
Date: Thu, 26 Apr 2012 13:58:57 +0300
Subject: [PATCH 15/44] Fixed a missing parameter in preg_match_all
Third parameter in preg_match_all is mandatory for PHP <5.4
---
vCard.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/vCard.php b/vCard.php
index 87ba6ffe..f1c68cc2 100644
--- a/vCard.php
+++ b/vCard.php
@@ -89,8 +89,9 @@ public function __construct($Path = false, $RawData = false)
// Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
// If there is only one, this is a single vCard, if more, multiple vCards are combined.
- $vCardBeginCount = preg_match_all('{^BEGIN\:VCARD}miS', $this -> RawData);
- $vCardEndCount = preg_match_all('{^END\:VCARD}miS', $this -> RawData);
+ $Matches = array();
+ $vCardBeginCount = preg_match_all('{^BEGIN\:VCARD}miS', $this -> RawData, $Matches);
+ $vCardEndCount = preg_match_all('{^END\:VCARD}miS', $this -> RawData, $Matches);
if (($vCardBeginCount != $vCardEndCount) || !$vCardBeginCount)
{
From c43e8a1496eae64a90cb31da6e85b5b4b91f4771 Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 05:55:40 +0300
Subject: [PATCH 16/44] Collapse option, fixed example card
Introduced the Collapse option
---
Example3.0.vcf | 4 ++--
README.md | 43 +++++++++++++++++++++++++++++++++++++++++++
test.php | 10 +++++++++-
vCard.php | 26 ++++++++++++++++++++++++--
4 files changed, 78 insertions(+), 5 deletions(-)
create mode 100644 README.md
diff --git a/Example3.0.vcf b/Example3.0.vcf
index 0ce82d7c..229d0ebf 100644
--- a/Example3.0.vcf
+++ b/Example3.0.vcf
@@ -4,7 +4,7 @@ N:Gump;Forrest
FN:Forrest Gump
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
-PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
+PHOTO;VALUE=URL;TYPE=GIF:http://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Example_svg.svg/200px-Example_svg.svg.png
TEL;TYPE=WORK,VOICE:(111) 555-1212
TEL;TYPE=HOME,VOICE:(404) 555-1212
TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
@@ -19,7 +19,7 @@ AGENT:BEGIN:VCARD
FN:Forrest Gump
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
- PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
+ PHOTO;VALUE=URL;TYPE=GIF:http://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Example_svg.svg/200px-Example_svg.svg.png
TEL;TYPE=WORK,VOICE:(111) 555-1212
TEL;TYPE=HOME,VOICE:(404) 555-1212
TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..7db6445e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+Nuovo/Nouveau vCard-parser is a simple vCard file parser with the focus on ease of use.
+
+The parser was written mostly because I couldn't find one that I was satisfied with - all those that I tried either failed with real world data or were too unwieldy or inconvenient, hence this parser.
+
+The parser can read both single and multiple vCards from a single file and with the help of PHP's magic methods and interfaces it can be written concisely. For example:
+
+``$vCard = new vCard('Example3.0.vcf');
+
+// Get the number of vCards in the file
+echo count($vCard);
+
+// In the single-vCard mode every element is accessible directly.
+if (count($vCard) == 1)
+{
+ print_r($vCard -> n);
+ print_r($vCard -> tel);
+}
+// In the multiple-vCard mode the object can be used as an array to retrieve separate vCard objects
+// for each vCard in the file.
+else
+{
+ foreach ($vCard as $vCardPart)
+ {
+ print_r($vCardPart -> n);
+ print_r($vCardPart -> tel);
+ }
+}``
+
+Every vCard element is accessible as an object member by the vCard element name. Every element is an array with the data parsed out of the file.
+It is possible to specify an option to the vCard constructor that will let you access every element as a single value in cases where there is just one value, e.g.:
+
+$vCard = new vCard('Example3.0.vcf', false, array('Collapse' => true));
+
+More on usage in [the wiki](https://github.com/nuovo/vCard-parser/wiki)
+
+See also:
+* http://tools.ietf.org/html/rfc2425
+* http://tools.ietf.org/html/rfc2426
+
+TODOs planned:
+* Add support for non-standard ("X-...") elements;
+
+http://www.nuovo.lv
\ No newline at end of file
diff --git a/test.php b/test.php
index 07ba13c0..bdbb8d44 100644
--- a/test.php
+++ b/test.php
@@ -1,7 +1,15 @@
true
+ )
+ );
if (count($vCard) == 0)
{
diff --git a/vCard.php b/vCard.php
index f1c68cc2..2b166db8 100644
--- a/vCard.php
+++ b/vCard.php
@@ -5,7 +5,7 @@
* @link https://github.com/nuovo/vCard-parser
* @author Roberts Bruveris, Martins Pilsetnieks
* @see RFC 2426, RFC 2425
- * @version 0.4
+ * @version 0.4.1
*/
class vCard implements Countable, Iterator
{
@@ -23,6 +23,15 @@ class vCard implements Countable, Iterator
private $Path = '';
private $RawData = '';
+ /**
+ * @var array Internal options container. Options:
+ * bool Collapse: If true, elements that can have multiple values but have only a single value are returned as that value instead of an array
+ * If false, an array is returned even if it has only one value.
+ */
+ private $Options = array(
+ 'Collapse' => false
+ );
+
/**
* @var array Internal data container. Contains vCard objects for multiple vCards and just the data for single vCards.
*/
@@ -53,10 +62,13 @@ class vCard implements Countable, Iterator
*
* @param string Path to file, optional.
* @param string Raw data, optional.
+ * @param array Additional options, optional. Currently supported options:
+ * bool Collapse: If true, elements that can have multiple values but have only a single value are returned as that value instead of an array
+ * If false, an array is returned even if it has only one value.
*
* One of these parameters must be provided, otherwise an exception is thrown.
*/
- public function __construct($Path = false, $RawData = false)
+ public function __construct($Path = false, $RawData = false, array $Options = null)
{
// Checking preconditions for the parser.
// If path is given, the file should be accessible.
@@ -87,6 +99,11 @@ public function __construct($Path = false, $RawData = false)
return true;
}
+ if ($Options)
+ {
+ $this -> Options = array_merge($this -> Options, $Options);
+ }
+
// Counting the begin/end separators. If there aren't any or the count doesn't match, there is a problem with the file.
// If there is only one, this is a single vCard, if more, multiple vCards are combined.
$Matches = array();
@@ -277,6 +294,11 @@ public function __get($Key)
}
return $Value;
}
+
+ if ($this -> Options['Collapse'] && is_array($this -> Data[$Key]) && (count($this -> Data[$Key]) == 1))
+ {
+ return $this -> Data[$Key][0];
+ }
return $this -> Data[$Key];
}
elseif ($Key == 'Mode')
From 9d529db5c529335a4000442d50bcec261e4088da Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 05:56:07 +0300
Subject: [PATCH 17/44] Cleanup
Cleanup
---
README | 38 --------------------------------------
1 file changed, 38 deletions(-)
delete mode 100644 README
diff --git a/README b/README
deleted file mode 100644
index 131e6be1..00000000
--- a/README
+++ /dev/null
@@ -1,38 +0,0 @@
-Nuovo/Nouveau vCard-parser is a simple vCard file parser with the focus on ease of use.
-
-The parser was written mostly because I couldn't find one that I was satisfied with - all those that I tried either failed with real world data or were too unwieldy or inconvenient, hence this parser.
-
-The parser can read both single and multiple vCards from a single file and with the help of PHP's magic methods and interfaces it can be written concisely. For example:
-
-$vCard = new vCard('Example3.0.vcf');
-
-// Get the number of vCards in the file
-echo count($vCard);
-
-// In the single-vCard mode every element is accessible directly.
-if (count($vCard) == 1)
-{
- print_r($vCard -> n);
- print_r($vCard -> tel);
-}
-// In the multiple-vCard mode the object can be used as an array to retrieve separate vCard objects
-// for each vCard in the file.
-else
-{
- foreach ($vCard as $vCardPart)
- {
- print_r($vCardPart -> n);
- print_r($vCardPart -> tel);
- }
-}
-
-Every vCard element is accessible as an object member by the vCard element name. Every element is an array with the data parsed out of the file.
-
-See also:
-- http://tools.ietf.org/html/rfc2425
-- http://tools.ietf.org/html/rfc2426
-
-TODOs planned:
-- Add support for non-standard ("X-...") elements;
-
-http://www.nuovo.lv
\ No newline at end of file
From 376483ceb25017030d9b152aac6b117704116782 Mon Sep 17 00:00:00 2001
From: Martins Pilsetnieks
Date: Mon, 30 Apr 2012 06:01:22 +0300
Subject: [PATCH 18/44] Cleanup
---
README.md | 47 +++++++++++++++++++++++++----------------------
1 file changed, 25 insertions(+), 22 deletions(-)
diff --git a/README.md b/README.md
index 7db6445e..f749355d 100644
--- a/README.md
+++ b/README.md
@@ -4,32 +4,35 @@ The parser was written mostly because I couldn't find one that I was satisfied w
The parser can read both single and multiple vCards from a single file and with the help of PHP's magic methods and interfaces it can be written concisely. For example:
-``$vCard = new vCard('Example3.0.vcf');
-
-// Get the number of vCards in the file
-echo count($vCard);
-
-// In the single-vCard mode every element is accessible directly.
-if (count($vCard) == 1)
-{
- print_r($vCard -> n);
- print_r($vCard -> tel);
-}
-// In the multiple-vCard mode the object can be used as an array to retrieve separate vCard objects
-// for each vCard in the file.
-else
-{
- foreach ($vCard as $vCardPart)
- {
- print_r($vCardPart -> n);
- print_r($vCardPart -> tel);
- }
-}``
+ $vCard = new vCard('Example3.0.vcf');
+
+Get the number of vCards in the file
+
+ echo count($vCard);
+
+In the single-vCard mode every element is accessible directly.
+
+ if (count($vCard) == 1)
+ {
+ print_r($vCard -> n);
+ print_r($vCard -> tel);
+ }
+
+In the multiple-vCard mode the object can be used as an array to retrieve separate vCard objects for each vCard in the file.
+
+ else
+ {
+ foreach ($vCard as $vCardPart)
+ {
+ print_r($vCardPart -> n);
+ print_r($vCardPart -> tel);
+ }
+ }
Every vCard element is accessible as an object member by the vCard element name. Every element is an array with the data parsed out of the file.
It is possible to specify an option to the vCard constructor that will let you access every element as a single value in cases where there is just one value, e.g.:
-$vCard = new vCard('Example3.0.vcf', false, array('Collapse' => true));
+ $vCard = new vCard('Example3.0.vcf', false, array('Collapse' => true));
More on usage in [the wiki](https://github.com/nuovo/vCard-parser/wiki)
From c58fc3a84aba52d306132be9691b7c450e4dcbc2 Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 06:32:07 +0300
Subject: [PATCH 19/44] readme
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index f749355d..da85545e 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@ The parser was written mostly because I couldn't find one that I was satisfied w
The parser can read both single and multiple vCards from a single file and with the help of PHP's magic methods and interfaces it can be written concisely. For example:
+ include('vCard.php');
$vCard = new vCard('Example3.0.vcf');
Get the number of vCards in the file
From 1c69f2f17b83ed9bfbec9b83eab1b19eea74532f Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 14:49:59 +0300
Subject: [PATCH 20/44] Minor fixes, new Example
---
.gitignore | 2 +
Example.vcf | 22 ++++++++
test.php | 147 ++++++++++++++++++++++++++++++++++++----------------
vCard.php | 16 ++++--
4 files changed, 137 insertions(+), 50 deletions(-)
create mode 100644 .gitignore
create mode 100644 Example.vcf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..9bea4330
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+
+.DS_Store
diff --git a/Example.vcf b/Example.vcf
new file mode 100644
index 00000000..9630a17b
--- /dev/null
+++ b/Example.vcf
@@ -0,0 +1,22 @@
+BEGIN:VCARD
+VERSION:3.0
+N:Doe;John;Q.,Public
+FN:John Doe
+TEL;TYPE=WORK,VOICE:(111) 555-1212
+TEL;TYPE=HOME,VOICE:(404) 555-1212
+TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
+EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
+EMAIL;TYPE=INTERNET:example@example.com
+PHOTO;VALUE=URL;TYPE=PNG:http://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Example_svg.svg/200px-Example_svg.svg.png
+AGENT:BEGIN:VCARD
+ VERSION:3.0
+ N:Doe;John;Q.,Public
+ FN:John Doe
+ TEL;TYPE=WORK,VOICE:(111) 555-1212
+ TEL;TYPE=HOME,VOICE:(404) 555-1212
+ TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
+ EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
+ EMAIL;TYPE=INTERNET:example@example.com
+ PHOTO;VALUE=URL;TYPE=PNG:http://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Example_svg.svg/200px-Example_svg.svg.png
+ END:VCARD
+END:VCARD
\ No newline at end of file
diff --git a/test.php b/test.php
index bdbb8d44..f86529ea 100644
--- a/test.php
+++ b/test.php
@@ -1,38 +1,44 @@
- true
- )
- );
-
- if (count($vCard) == 0)
+
+
+
+
+
+
- print_r($vCard -> agent);
+'.$vCard -> FN[0].'';
- if ($vCard -> photo)
+ if ($vCard -> PHOTO)
{
- // If there is a photo or a logo, or a sound embedded in the file,
- // it can be accessed directly, for example:
- foreach ($vCard -> photo as $Photo)
+ foreach ($vCard -> PHOTO as $Photo)
{
- // You have to take into account that the file can also be a URI (in that case the Encoding parameter will be "uri" instead of "b"
if ($Photo['Encoding'] == 'b')
{
echo '
';
@@ -41,36 +47,87 @@
{
echo '
';
}
+
+ /*
+ // It can also be saved to a file
+ try
+ {
+ $vCard -> SaveFile('photo', 0, 'test_image.jpg');
+ // The parameters are:
+ // - name of the file we want to save (photo, logo or sound)
+ // - index of the file in case of multiple files (defaults to 0)
+ // - target path to save to, including the filenam
+ }
+ catch (Exception $E)
+ {
+ // Target path not writable
+ }
+ */
}
+ }
- // It can also be saved to a file
- try
+ foreach ($vCard -> N as $Name)
+ {
+ echo 'Name: '.$Name['FirstName'].' '.$Name['LastName'].'
';
+ }
+
+ if ($vCard -> TEL)
+ {
+ echo 'Phone
';
+ foreach ($vCard -> TEL as $Tel)
+ {
+ echo $Tel['Value'].' ('.implode(', ', $Tel['Type']).')
';
+ }
+ echo '
';
+ }
+
+ if ($vCard -> EMAIL)
+ {
+ echo 'Email
';
+ foreach ($vCard -> EMAIL as $Email)
{
- $vCard -> SaveFile('photo', 0, 'test_image.jpg');
- // The parameters are:
- // - name of the file we want to save (photo, logo or sound)
- // - index of the file in case of multiple files (defaults to 0)
- // - target path to save to, including the filenam
+ echo $Email['Value'].' ('.implode(', ', $Email['Type']).')
';
}
- catch (Exception $E)
+ echo '';
+ }
+
+ if ($vCard -> AGENT)
+ {
+ echo 'Agents
';
+ foreach ($vCard -> AGENT as $Agent)
{
- // Target path not writable
+ echo '';
+ OutputvCard($Agent);
+ echo '
';
}
}
}
+
+ $vCard = new vCard(
+ 'Example.vcf', // Path to vCard file
+ false, // Raw vCard text, can be used instead of a file
+ array( // Option array
+ // This lets you get single values for elements that could contain multiple values but have only one value.
+ // This defaults to false so every value that could have multiple values is returned as array.
+ 'Collapse' => false
+ )
+ );
+
+ if (count($vCard) == 0)
+ {
+ throw new Exception('vCard test: empty vCard!');
+ }
+ // if the file contains a single vCard, it is accessible directly.
+ elseif (count($vCard) == 1)
+ {
+ OutputvCard($vCard);
+ }
// if the file contains multiple vCards, they are accessible as elements of an array
else
{
foreach ($vCard as $Index => $vCardPart)
{
- echo '';
- print_r($vCardPart -> n);
- print_r($vCardPart -> tel);
- print_r($vCardPart -> email);
- print_r($vCardPart -> phone);
- print_r($vCardPart -> adr);
- echo '
';
- echo '
';
+ OutputvCard($vCard);
}
}
?>
\ No newline at end of file
diff --git a/vCard.php b/vCard.php
index 2b166db8..3fbf3fc9 100644
--- a/vCard.php
+++ b/vCard.php
@@ -275,6 +275,7 @@ public function __construct($Path = false, $RawData = false, array $Options = nu
*/
public function __get($Key)
{
+ $Key = strtolower($Key);
if (isset($this -> Data[$Key]))
{
if ($Key == 'agent')
@@ -305,7 +306,7 @@ public function __get($Key)
{
return $this -> Mode;
}
- return null;
+ return array();
}
/**
@@ -361,6 +362,8 @@ public function SaveFile($Key, $Index = 0, $TargetPath = '')
*/
public function __call($Key, $Arguments)
{
+ $Key = strtolower($Key);
+
if (!isset($this -> Data[$Key]))
{
$this -> Data[$Key] = array();
@@ -377,8 +380,8 @@ public function __call($Key, $Arguments)
{
$Types = array_values(array_slice($Arguments, 1));
- if (isset(self::$Spec_StructuredElements[strtolower($Key)]) &&
- in_array($Arguments[1], self::$Spec_StructuredElements[strtolower($Key)])
+ if (isset(self::$Spec_StructuredElements[$Key]) &&
+ in_array($Arguments[1], self::$Spec_StructuredElements[$Key])
)
{
$LastElementIndex = 0;
@@ -436,6 +439,7 @@ public function __toString()
foreach ($this -> Data as $Key => $Values)
{
$KeyUC = strtoupper($Key);
+ $Key = strtolower($Key);
if (in_array($KeyUC, array('PHOTO', 'VERSION')))
{
@@ -513,9 +517,11 @@ private static function ParseStructuredValue($Text, $Key)
$Text = array_map('trim', explode(';', $Text));
$Result = array();
- for ($i = 0; $i < count($Text) && $i < count(self::$Spec_StructuredElements[$Key]); $i++)
+ $Ctr = 0;
+
+ foreach (self::$Spec_StructuredElements[$Key] as $Index => $StructurePart)
{
- $Result[self::$Spec_StructuredElements[$Key][$i]] = $Text[$i];
+ $Result[$StructurePart] = isset($Text[$Index]) ? $Text[$Index] : null;
}
return $Result;
}
From 276d0fb5acaa53b3b7789094cf8bb85f6c49ea56 Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 14:59:10 +0300
Subject: [PATCH 21/44] Correct version number
---
vCard.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vCard.php b/vCard.php
index 3fbf3fc9..59e5fed6 100644
--- a/vCard.php
+++ b/vCard.php
@@ -5,7 +5,7 @@
* @link https://github.com/nuovo/vCard-parser
* @author Roberts Bruveris, Martins Pilsetnieks
* @see RFC 2426, RFC 2425
- * @version 0.4.1
+ * @version 0.4.2
*/
class vCard implements Countable, Iterator
{
From 36f338cf1fda9c8ac4fbba488bbda7e9779df0f2 Mon Sep 17 00:00:00 2001
From: Martins
Date: Mon, 30 Apr 2012 15:00:23 +0300
Subject: [PATCH 22/44] Cleanup
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 9bea4330..295469b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
.DS_Store
+
+Downloads/*
From 2babfbdef8b0b62bc196464df67f8318a79477cb Mon Sep 17 00:00:00 2001
From: Martins
Date: Fri, 11 May 2012 02:20:00 +0300
Subject: [PATCH 23/44] Mostly bugfixes
Fixed a few bugs, test page now shows more elements from the vCard,
write-test.php now has an example of adding address.
---
Example.vcf | 4 +++-
Example2.1.vcf | 2 +-
test.php | 50 +++++++++++++++++++++++++++++++++++++++++++++-----
vCard.php | 28 ++++++++++++++--------------
write-test.php | 9 ++++++++-
5 files changed, 71 insertions(+), 22 deletions(-)
diff --git a/Example.vcf b/Example.vcf
index 9630a17b..e0fab50a 100644
--- a/Example.vcf
+++ b/Example.vcf
@@ -1,12 +1,14 @@
BEGIN:VCARD
VERSION:3.0
N:Doe;John;Q.,Public
-FN:John Doe
+FN;CHARSET=UTF-8:John Doe
TEL;TYPE=WORK,VOICE:(111) 555-1212
TEL;TYPE=HOME,VOICE:(404) 555-1212
TEL;TYPE=HOME,TYPE=VOICE:(404) 555-1213
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
EMAIL;TYPE=INTERNET:example@example.com
+ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
+URL:https://www.google.com/
PHOTO;VALUE=URL;TYPE=PNG:http://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Example_svg.svg/200px-Example_svg.svg.png
AGENT:BEGIN:VCARD
VERSION:3.0
diff --git a/Example2.1.vcf b/Example2.1.vcf
index 5e88420a..016e31a2 100644
--- a/Example2.1.vcf
+++ b/Example2.1.vcf
@@ -10,6 +10,6 @@ ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America
ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, LA 30314=0D=0AUnited States of America
-EMAIL;PREF;INTERNET:forrestgump@example.com
+EMAIL;PREF;INTERNET;HOME:forrestgump@example.com
REV:20080424T195243Z
END:VCARD
\ No newline at end of file
diff --git a/test.php b/test.php
index f86529ea..96f7b17d 100644
--- a/test.php
+++ b/test.php
@@ -5,7 +5,7 @@
+
\ No newline at end of file
+?>
+
+