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 +?> + + \ No newline at end of file From ade4c4bb660a3f80bac9b7744a124d661c98b01a Mon Sep 17 00:00:00 2001 From: Martins Pilsetnieks Date: Fri, 14 Dec 2012 18:38:50 +0200 Subject: [PATCH 32/44] Removing unsupported parameters from file elements For example, Apple Address Book adds parameters that break file parsing --- vCard.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/vCard.php b/vCard.php index edca086c..dffee9b4 100644 --- a/vCard.php +++ b/vCard.php @@ -241,6 +241,18 @@ public function __construct($Path = false, $RawData = false, array $Options = nu } } + // Checking files for colon-separated additional parameters (Apple's Address Book does this), for example, "X-ABCROP-RECTANGLE" for photos + if (in_array($Key, self::$Spec_FileElements) && isset($Parameters['encoding']) && in_array($Parameters['encoding'], array('b', 'base64'))) + { + // If colon is present in the value, it must contain Address Book parameters + // (colon is an invalid character for base64 so it shouldn't appear in valid files) + if (strpos($Value, ':') !== false) + { + $Value = explode(':', $Value); + $Value = array_pop($Value); + } + } + // Values are parsed according to their type if (isset(self::$Spec_StructuredElements[$Key])) { From 41e684f7a5d2beb274c3e11db9ad72443ea9bb67 Mon Sep 17 00:00:00 2001 From: Martins Pilsetnieks Date: Fri, 14 Dec 2012 18:39:27 +0200 Subject: [PATCH 33/44] Removing unsupported parameters from file elements --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0e033037..2e9e70fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .DS_Store Downloads/* -MP.vcf -Issue12.vcf \ No newline at end of file +*.vcf From 73d8b6fad66b63e51e47a90b0f3ecb489a598a73 Mon Sep 17 00:00:00 2001 From: Martins Pilsetnieks Date: Fri, 14 Dec 2012 18:44:38 +0200 Subject: [PATCH 34/44] Version number increase --- vCard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vCard.php b/vCard.php index dffee9b4..254a83b9 100644 --- a/vCard.php +++ b/vCard.php @@ -5,7 +5,7 @@ * @link https://github.com/nuovo/vCard-parser * @author Martins Pilsetnieks, Roberts Bruveris * @see RFC 2426, RFC 2425 - * @version 0.4.7 + * @version 0.4.8 */ class vCard implements Countable, Iterator { From 860b27837d4407a7efb923dd27c3bc717a1c0e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=84rtin=CC=A7s=CC=8C=20Pilse=CC=84tnieks?= Date: Tue, 5 Feb 2013 19:26:12 +0200 Subject: [PATCH 35/44] Added MIT license Added MIT license --- LICENSE | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5e620222 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License + +Copyright (C) 2012 Nuovo + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From 7ffe5c7752865acf4a59a955c3aac6c7455e4000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=CC=84rtin=CC=A7s=CC=8C=20Pilse=CC=84tnieks?= Date: Sun, 24 Feb 2013 23:10:12 +0100 Subject: [PATCH 36/44] Extending vCard now possible Extending vCard is now possible (vCard objects were made internally for files that contained multiple cards and for cards that had AGENT elements.) --- vCard.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vCard.php b/vCard.php index 254a83b9..b2f04760 100644 --- a/vCard.php +++ b/vCard.php @@ -134,9 +134,10 @@ public function __construct($Path = false, $RawData = false, array $Options = nu { // 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); + + $ClassName = get_class($this); + $this -> Data[] = new $ClassName(false, $SinglevCardRawData); } } else @@ -181,7 +182,8 @@ public function __construct($Path = false, $RawData = false, array $Options = nu if ((strpos($Key, 'agent') === 0) && (stripos($Value, 'begin:vcard') !== false)) { - $Value = new vCard(false, str_replace('-wrap-', "\n", $Value)); + $ClassName = get_class($this); + $Value = new $ClassName(false, str_replace('-wrap-', "\n", $Value)); if (!isset($this -> Data[$Key])) { $this -> Data[$Key] = array(); From a5806d36c4288171b90f1f49f2eca8353b6ebfdc Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Thu, 14 Aug 2014 21:55:19 +0200 Subject: [PATCH 37/44] applied changes taken from http://www.ip-phone-forum.de/showthread.php?t=267477 --- vCard.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vCard.php b/vCard.php index b2f04760..a6c687c5 100644 --- a/vCard.php +++ b/vCard.php @@ -49,7 +49,7 @@ class vCard implements Countable, Iterator private static $Spec_MultipleValueElements = array('nickname', 'categories'); private static $Spec_ElementTypes = array( - 'email' => array('internet', 'x400', 'pref'), + 'email' => array('internet', 'x400', 'pref', 'home', 'work'), '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'), @@ -686,4 +686,4 @@ public function key() return key($this -> Data); } } -?> \ No newline at end of file +?> From b792826f47a31db284724a22d50095c46daf9f9d Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Fri, 15 Aug 2014 20:17:21 +0200 Subject: [PATCH 38/44] integrated changes from http://www.ip-phone-forum.de/showthread.php?t=267477 --- vCard.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vCard.php b/vCard.php index b2f04760..a6c687c5 100644 --- a/vCard.php +++ b/vCard.php @@ -49,7 +49,7 @@ class vCard implements Countable, Iterator private static $Spec_MultipleValueElements = array('nickname', 'categories'); private static $Spec_ElementTypes = array( - 'email' => array('internet', 'x400', 'pref'), + 'email' => array('internet', 'x400', 'pref', 'home', 'work'), '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'), @@ -686,4 +686,4 @@ public function key() return key($this -> Data); } } -?> \ No newline at end of file +?> From 1658c0e9e362a3d439c14d021119c42608ccd54a Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Sat, 16 Aug 2014 20:28:23 +0200 Subject: [PATCH 39/44] fixed problem in case count($Parameter) > 2 and if there is no "," separater but a quoted-printable string with "=" --- vCard.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vCard.php b/vCard.php index a6c687c5..dec83bb2 100644 --- a/vCard.php +++ b/vCard.php @@ -603,10 +603,13 @@ private static function ParseParameters($Key, array $RawParams = null) } elseif (count($Parameter) > 2) { - $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index])); - if ($TempTypeParams['type']) + if(!empty(explode(',', $RawParams[$Index], -1))) { - $Type = array_merge($Type, $TempTypeParams['type']); + $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index])); + if ($TempTypeParams['type']) + { + $Type = array_merge($Type, $TempTypeParams['type']); + } } } else From c928b81c0c95a2dff338802e3977942cd997a5c4 Mon Sep 17 00:00:00 2001 From: Jens Maus Date: Thu, 28 Aug 2014 20:30:31 +0200 Subject: [PATCH 40/44] use count() instead of empty() since explode() returns an array. --- vCard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vCard.php b/vCard.php index dec83bb2..1e19f80f 100644 --- a/vCard.php +++ b/vCard.php @@ -603,7 +603,7 @@ private static function ParseParameters($Key, array $RawParams = null) } elseif (count($Parameter) > 2) { - if(!empty(explode(',', $RawParams[$Index], -1))) + if(count(explode(',', $RawParams[$Index], -1)) > 0) { $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index])); if ($TempTypeParams['type']) From 38ac2ea530a1c623ac0751487617df74d8b68190 Mon Sep 17 00:00:00 2001 From: Jim Jagielski Date: Mon, 9 Feb 2015 17:00:40 -0500 Subject: [PATCH 41/44] Force all keys to be lowercase, and allow for lowercase use in __call. eg: $v->n('Barski', 'LastName'); $v->n('Foo', 'firstname'); work fine (no need to case-match) --- test.php | 42 +++++++++++++++++++++--------------------- vCard.php | 44 ++++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/test.php b/test.php index 7b2ae9b8..71015e62 100644 --- a/test.php +++ b/test.php @@ -1,4 +1,4 @@ - + @@ -40,13 +40,13 @@ function OutputvCard(vCard $vCard) { foreach ($vCard -> PHOTO as $Photo) { - if ($Photo['Encoding'] == 'b') + if ($Photo['encoding'] == 'b') { - echo '
'; + echo '
'; } else { - echo '
'; + echo '
'; } /* @@ -69,14 +69,14 @@ function OutputvCard(vCard $vCard) foreach ($vCard -> N as $Name) { - echo '

Name: '.$Name['FirstName'].' '.$Name['LastName'].'

'; + echo '

Name: '.$Name['firstname'].' '.$Name['lastname'].'

'; } foreach ($vCard -> ORG as $Organization) { - echo '

Organization: '.$Organization['Name']. - ($Organization['Unit1'] || $Organization['Unit2'] ? - ' ('.implode(', ', array($Organization['Unit1'], $Organization['Unit2'])).')' : + echo '

Organization: '.$Organization['name']. + ($Organization['unit1'] || $Organization['unit2'] ? + ' ('.implode(', ', array($Organization['unit1'], $Organization['unit2'])).')' : '' ).'

'; } @@ -92,7 +92,7 @@ function OutputvCard(vCard $vCard) } else { - echo $Tel['Value'].' ('.implode(', ', $Tel['Type']).')
'; + echo $Tel['value'].' ('.implode(', ', $Tel['type']).')
'; } } echo '

'; @@ -109,7 +109,7 @@ function OutputvCard(vCard $vCard) } else { - echo $Email['Value'].' ('.implode(', ', $Email['Type']).')
'; + echo $Email['value'].' ('.implode(', ', $Email['type']).')
'; } } echo '

'; @@ -126,7 +126,7 @@ function OutputvCard(vCard $vCard) } else { - echo $URL['Value'].'
'; + echo $URL['value'].'
'; } } echo '

'; @@ -143,7 +143,7 @@ function OutputvCard(vCard $vCard) } else { - echo $IMPP['Value'].'
'; + echo $IMPP['value'].'
'; } } echo '

'; @@ -153,14 +153,14 @@ function OutputvCard(vCard $vCard) { foreach ($vCard -> ADR as $Address) { - echo '

Address ('.implode(', ', $Address['Type']).')

'; - echo 'Street address: '.($Address['StreetAddress'] ? $Address['StreetAddress'] : '-').'
'. - 'PO Box: '.($Address['POBox'] ? $Address['POBox'] : '-').'
'. - 'Extended address: '.($Address['ExtendedAddress'] ? $Address['ExtendedAddress'] : '-').'
'. - 'Locality: '.($Address['Locality'] ? $Address['Locality'] : '-').'
'. - 'Region: '.($Address['Region'] ? $Address['Region'] : '-').'
'. - 'ZIP/Post code: '.($Address['PostalCode'] ? $Address['PostalCode'] : '-').'
'. - 'Country: '.($Address['Country'] ? $Address['Country'] : '-').''; + echo '

Address ('.implode(', ', $Address['type']).')

'; + echo 'Street address: '.($Address['streetaddress'] ? $Address['streetaddress'] : '-').'
'. + 'PO Box: '.($Address['pobox'] ? $Address['pobox'] : '-').'
'. + 'Extended address: '.($Address['extendedaddress'] ? $Address['extendedaddress'] : '-').'
'. + 'Locality: '.($Address['locality'] ? $Address['locality'] : '-').'
'. + 'Region: '.($Address['region'] ? $Address['region'] : '-').'
'. + 'ZIP/Post code: '.($Address['postalcode'] ? $Address['postalcode'] : '-').'
'. + 'Country: '.($Address['country'] ? $Address['country'] : '-').''; } echo '

'; } @@ -213,4 +213,4 @@ function OutputvCard(vCard $vCard) } ?> - \ No newline at end of file + diff --git a/vCard.php b/vCard.php index b2f04760..e069cb4b 100644 --- a/vCard.php +++ b/vCard.php @@ -41,10 +41,10 @@ class vCard implements Countable, Iterator * @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') + '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'); @@ -261,7 +261,7 @@ public function __construct($Path = false, $RawData = false, array $Options = nu $Value = self::ParseStructuredValue($Value, $Key); if ($Type) { - $Value['Type'] = $Type; + $Value['type'] = $Type; } } else @@ -274,15 +274,15 @@ public function __construct($Path = false, $RawData = false, array $Options = nu if ($Type) { $Value = array( - 'Value' => $Value, - 'Type' => $Type + 'value' => $Value, + 'type' => $Type ); } } if (is_array($Value) && $Encoding) { - $Value['Encoding'] = $Encoding; + $Value['encoding'] = $Encoding; } if (!isset($this -> Data[$Key])) @@ -317,10 +317,10 @@ public function __get($Key) $Value = $this -> Data[$Key]; foreach ($Value as $K => $V) { - if (stripos($V['Value'], 'uri:') === 0) + if (stripos($V['value'], 'uri:') === 0) { - $Value[$K]['Value'] = substr($V, 4); - $Value[$K]['Encoding'] = 'uri'; + $Value[$K]['value'] = substr($V, 4); + $Value[$K]['encoding'] = 'uri'; } } return $Value; @@ -360,15 +360,15 @@ public function SaveFile($Key, $Index = 0, $TargetPath = '') } // Returing false if it is an image URL - if (stripos($this -> Data[$Key][$Index]['Value'], 'uri:') === 0) + 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 = $this -> Data[$Key][$Index]['value']; + if (isset($this -> Data[$Key][$Index]['encoding']) && $this -> Data[$Key][$Index]['encoding'] == 'b') { $RawContent = base64_decode($RawContent); } @@ -403,10 +403,10 @@ public function __call($Key, $Arguments) if (count($Arguments) > 1) { - $Types = array_values(array_slice($Arguments, 1)); + $Types = array_map('strtolower', array_values(array_slice($Arguments, 1))); if (isset(self::$Spec_StructuredElements[$Key]) && - in_array($Arguments[1], self::$Spec_StructuredElements[$Key]) + in_array(strtolower($Arguments[1]), self::$Spec_StructuredElements[$Key]) ) { $LastElementIndex = 0; @@ -438,8 +438,8 @@ public function __call($Key, $Arguments) elseif (isset(self::$Spec_ElementTypes[$Key])) { $this -> Data[$Key][] = array( - 'Value' => $Value, - 'Type' => $Types + 'value' => $Value, + 'type' => $Types ); } } @@ -474,9 +474,9 @@ public function __toString() foreach ($Values as $Index => $Value) { $Text .= $KeyUC; - if (is_array($Value) && isset($Value['Type'])) + if (is_array($Value) && isset($Value['type'])) { - $Text .= ';TYPE='.self::PrepareTypeStrForOutput($Value['Type']); + $Text .= ';TYPE='.self::PrepareTypeStrForOutput($Value['type']); } $Text .= ':'; @@ -491,7 +491,7 @@ public function __toString() } elseif (is_array($Value) && isset(self::$Spec_ElementTypes[$Key])) { - $Text .= $Value['Value']; + $Text .= $Value['value']; } else { @@ -686,4 +686,4 @@ public function key() return key($this -> Data); } } -?> \ No newline at end of file +?> From 5509cfa012bd97b05acbafcd6d836bae7817b5f0 Mon Sep 17 00:00:00 2001 From: Jim Jagielski Date: Mon, 9 Feb 2015 17:03:58 -0500 Subject: [PATCH 42/44] no ending tag (PSR) --- vCard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vCard.php b/vCard.php index e069cb4b..af26ae68 100644 --- a/vCard.php +++ b/vCard.php @@ -686,4 +686,4 @@ public function key() return key($this -> Data); } } -?> + From 4b563ff7d495bc4349a85bc69688b41972cd42ff Mon Sep 17 00:00:00 2001 From: Jim Jagielski Date: Wed, 11 Feb 2015 12:13:03 -0500 Subject: [PATCH 43/44] W/ suggestions from http://www.ip-phone-forum.de/showthread.php?t=267477 --- vCard.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vCard.php b/vCard.php index af26ae68..f4cdb102 100644 --- a/vCard.php +++ b/vCard.php @@ -49,7 +49,7 @@ class vCard implements Countable, Iterator private static $Spec_MultipleValueElements = array('nickname', 'categories'); private static $Spec_ElementTypes = array( - 'email' => array('internet', 'x400', 'pref'), + 'email' => array('internet', 'x400', 'pref', 'home', 'work'), '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'), @@ -686,4 +686,3 @@ public function key() return key($this -> Data); } } - From 6422f36ddc90e1d31b2a87fa84711470e85278ef Mon Sep 17 00:00:00 2001 From: Jim Jagielski Date: Wed, 11 Feb 2015 12:18:39 -0500 Subject: [PATCH 44/44] Be aware of ',' and its lack thereof --- vCard.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vCard.php b/vCard.php index f4cdb102..f10b729c 100644 --- a/vCard.php +++ b/vCard.php @@ -603,10 +603,13 @@ private static function ParseParameters($Key, array $RawParams = null) } elseif (count($Parameter) > 2) { - $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index])); - if ($TempTypeParams['type']) + if(count(explode(',', $RawParams[$Index], -1))) { - $Type = array_merge($Type, $TempTypeParams['type']); + $TempTypeParams = self::ParseParameters($Key, explode(',', $RawParams[$Index])); + if ($TempTypeParams['type']) + { + $Type = array_merge($Type, $TempTypeParams['type']); + } } } else