From b4633832bba81bdfff643968a80b266f575a5258 Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:23:42 -0500 Subject: [PATCH 01/17] Update File/IMC/Build/Vcard.php Oops I broke all the add*() methods such that they only set the first iteration addEmail('user1@test.com'); addEmail('user2@test.com'); // should not be overwriting the above! --- File/IMC/Build/Vcard.php | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/File/IMC/Build/Vcard.php b/File/IMC/Build/Vcard.php index 0c9bb20..0cd4845 100644 --- a/File/IMC/Build/Vcard.php +++ b/File/IMC/Build/Vcard.php @@ -54,7 +54,7 @@ */ class File_IMC_Build_Vcard extends File_IMC_Build { - /** + /** * Constructor * * @param string $version The vCard version to build; affects which @@ -411,10 +411,19 @@ protected function _setORG($value,$iter) unset($value[$k]); } } + // flatten the array + $vals = $value; + $value = array(); + foreach ( $vals as $v ) + { + settype($v,'array'); + $value = array_merge($value,$v); + } + // clear existing value if ( isset($this->value['ORG'][$iter]) ) { - // clear existing value unset($this->value['ORG'][$iter]); } + // set the new value(s) foreach ( $value as $k => $v) { settype($v, 'array'); foreach ( $v as $v2 ) { @@ -639,7 +648,6 @@ public function fetch() 'CATEGORIES'=> array( 'vers' => array('3.0') ), 'NOTE' => array( ), 'PRODID' => array( 'vers' => array('3.0') ), - 'CLASS' => array( 'vers' => array('3.0') ), 'REV' => array( ), 'SORT-STRING'=>array( 'vers' => array('3.0') ), 'SOUND' => array( ), @@ -715,7 +723,7 @@ public function fetch() */ public function addAddress() { $args = func_get_args(); - return $this->set('ADR',$args); + return $this->set('ADR',$args,'new'); } /** @@ -723,7 +731,7 @@ public function addAddress() { * @see self::set() */ public function addCategories($val) { - return $this->set('CATEGORIES',$val); + return $this->set('CATEGORIES',$val,'new'); } /** @@ -731,7 +739,7 @@ public function addCategories($val) { * @see self::set() */ public function addEmail($val) { - return $this->set('EMAIL',$val); + return $this->set('EMAIL',$val,'new'); } /** @@ -739,7 +747,7 @@ public function addEmail($val) { * @see self::set() */ public function addLabel($val) { - return $this->set('LABEL',$val); + return $this->set('LABEL',$val,'new'); } /** @@ -747,14 +755,23 @@ public function addLabel($val) { * @see self::set() */ public function addNickname($val) { - return $this->set('NICKNAME',$val); + return $this->set('NICKNAME',$val,'new'); } /** * @deprecated * @see self::set() */ - public function addOrganization($val) { + public function addOrganization($val,$append=true) { + if ( $append && !empty($this->value['ORG'][0]) ) + { + settype($val,'array'); + $vals_cur = array(); + foreach ( $this->value['ORG'][0] as $part_num => $part_val ) { + $vals_cur = array_merge($vals_cur,$part_val); + } + $val = array_merge($vals_cur,$val); + } return $this->set('ORG',$val); } @@ -763,7 +780,7 @@ public function addOrganization($val) { * @see self::set() */ public function addTelephone($val) { - return $this->set('TEL',$val); + return $this->set('TEL',$val,'new'); } /** From a8edc553670ab8ce281dda3bd3693d7e5669e87c Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:26:07 -0500 Subject: [PATCH 02/17] Update tests/AllTests.php --- tests/AllTests.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/AllTests.php b/tests/AllTests.php index df07418..121d617 100644 --- a/tests/AllTests.php +++ b/tests/AllTests.php @@ -1,4 +1,5 @@ \ No newline at end of file From 28a0f10b10b43c4b771e6d942ee6a3a4c50373bd Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:29:05 -0500 Subject: [PATCH 03/17] Update tests/File/IMC/BuildTest.php tests set() for all standard properties and tests all add*() and set*() methods. tests still need to be created to test that get() / fetch() return properly escaped and encoded strings --- tests/File/IMC/BuildTest.php | 199 +++++++++++++++++++++++++++++++++-- 1 file changed, 191 insertions(+), 8 deletions(-) diff --git a/tests/File/IMC/BuildTest.php b/tests/File/IMC/BuildTest.php index ba929b4..0ab899c 100644 --- a/tests/File/IMC/BuildTest.php +++ b/tests/File/IMC/BuildTest.php @@ -61,6 +61,189 @@ public function setUp() $this->vcard = File_IMC::build('vcard'); } + /** + * Data provider for {@link self::testExampleParser()}. + * + * each array consists of + * PropertyName + * Value(s) + * Deprecated Method to test (or null) + * Alternate Values (or null)... will test that stored value is same as 1st set of values + * + * @return array + */ + public function exampleProvider() + { + $data = array( + array('VERSION', '3.0', 'setVersion'), + array('FN', 'Mr. John Q. Public, Esq.', 'setFormattedName'), + array('N', + array( + 'Stevenson', + 'John', + array('Philip','Paul'), + 'Dr.', + array('Jr.','M.D.','A.C.P.') + ), + 'setName', + array( + 'honorific-prefix' => 'Dr.', + 'given-name' => 'John', + 'additional-name' => array('Philip','Paul'), + 'family-name' => 'Stevenson', + 'honorific-suffix' => array('Jr.','M.D.','A.C.P.'), + ) + ), + array('PROFILE', 'vcard'), // formerly no "setProfile" method + array('NAME', 'RFC 2426 - vCard MIME Directory Profile', 'setSourceName'), + array('SOURCE', 'http://tools.ietf.org/html/rfc2426', 'setSource'), + array('NICKNAME', 'Robbie', 'addNickname'), + array('PHOTO', dirname(__FILE__).'/../../test_pattern.gif','setPhoto'), + array('BDAY', '1996-04-15', 'setBirthday'), + array('ADR', + array( + '', + '', + '123 Main Street', + 'Any Town', + 'CA', + '91921-1234', + ), + 'addAddress', + array( + 'street-address' => '123 Main Street', + 'locality' => 'Any Town', + 'region' => 'CA', + 'postal-code' => '91921-1234', + ) + ), + array('LABEL', "Mr.John Q. Public, Esq.\nMail Drop: TNE QB\n123 Main Street\nAny Town, CA 91921-1234\nU.S.A.", 'addLabel'), + array('TEL', '+1-213-555-1234', 'addTelephone'), + array('EMAIL', 'jqpublic@xyz.dom1.com', 'addEmail'), + array('MAILER', 'PigeonMail 2.1', 'setMailer'), + array('TZ', '-05:00', 'setTZ'), + array('GEO', + array( + '37.386013', + '-122.082932', + ), + 'setGeo', + array( + 'latitude' => '37.386013', + 'longitude' => '-122.082932', + ) + ), + array('TITLE', 'Director, Research and Development', 'setTitle'), + array('ROLE', 'Programmer', 'setRole'), + array('LOGO', '', 'setLogo'), + array('AGENT', '', 'setAgent'), + array('ORG', + array( + 'ABC, Inc.', + 'North American Division', + 'Marketing' + ), + 'addOrganization', + array( + 'organization-name' => 'ABC, Inc.', + 'organization-unit' => array('North American Division','Marketing'), + ) + ), + array('CATEGORIES', array( + 'INTERNET', + 'IETF', + 'INDUSTRY', + 'INFORMATION TECHNOLOGY', + ), 'addCategories'), + array('NOTE', 'This fax number is operational 0800 to 1715 EST, Mon-Fri.', 'setNote'), + array('PRODID', '-//ONLINE DIRECTORY//NONSGML Version 1//EN', 'setProductID'), + array('REV', '1995-10-31T22:27:10Z', 'setRevision'), + array('SORT-STRING','Public John Q', 'setSortString'), + array('SOUND', '', 'setSound'), + array('UID', '19950401-080045-40000F192713-0052', 'setUniqueID'), + array('URL', 'http://pear.php.net/package/File_IMC/', 'setURL'), + array('CLASS', 'PUBLIC', 'setClass'), + array('KEY', '', 'setKey'), + ); + return $data; + } + + /** + * test that the values are properly stored + * this does not test that the values are properly fetched (escaped and encoded) + * + * @todo... add an expected get() result string to the dataProvider to test the get() method + * + * @param string $propName The parameter to set + * @param string $propValue The value + * @return void + * + * @dataProvider exampleProvider + */ + public function testSettingValues($propName,$passedValue,$depMethod=null,$propValueAlt=null) + { + for ( $i=0; $i<2; $i++ ) + { + $propValue = $passedValue; // set/reset + $iter = 0; + // clear any previously stored value + if ( isset($this->value[$propName][$iter]) ) { + unset($this->value[$propName][$iter]); + } + if ( $i == 0 ) { + // test the set($propname) method + $this->vcard->set($propName,$propValue); + } + else { + // test the deprecated method (if specified) + if ( empty($depMethod) ) + continue; + if ( in_array($propName,array('N','ADR','GEO')) ) { + // multiple parameters + call_user_func_array(array($this->vcard,$depMethod), $propValue); + } + else { + // single parameter + call_user_func(array($this->vcard,$depMethod), $propValue); + } + } + // test that value correctly stored + if ( in_array($propName,array('N','ADR','GEO','ORG')) ) { + // multiple parts + foreach ( $propValue as $part => $val ) { + $valSet = $this->vcard->value[$propName][$iter][$part]; + settype($val, 'array'); + $this->assertSame($valSet, $val); + } + } + else { + // one part / multiple repetitions + $valSet = $this->vcard->value[$propName][$iter][0]; + if ( in_array($propName,array('PHOTO','LOGO','SOUND','KEY')) ) + { + if ( file_exists($propValue) ) { + $propValue = base64_encode(file_get_contents($propValue)); + $this->assertSame('B', $this->vcard->param[$propName][$iter]['ENCODING'][0]); + } + elseif ( preg_match('#^(https?|ftp)://#',$propValue) ) { + $this->assertSame('URI', $this->vcard->param[$propName][$iter]['VALUE'][0]); + } + } + settype($propValue, 'array'); + $this->assertSame($valSet, $propValue); + } + } + if ( !empty($propValueAlt) ) { + // test the alternate method creates the same value + $iter = 0; + $this->vcard->set($propName,$propValue); + $valSetA = $this->vcard->value[$propName][$iter]; + $this->vcard->set($propName,$propValueAlt); + $valSetB = $this->vcard->value[$propName][$iter]; + $this->assertSame($valSetA, $valSetB); + } + } + /** * @expectedException File_IMC_Exception */ @@ -82,8 +265,8 @@ public function testExceptionIfInvalidFormatIsProvided() */ public function testFluentInterface() { - $this->assertInstanceOf('File_IMC_Build_Vcard', $this->vcard->setName('Doe', 'John')); - $this->assertInstanceOf('File_IMC_Build_Vcard', $this->vcard->setSource('Your mom.')); + $this->assertInstanceOf('File_IMC_Build_Vcard', $this->vcard->set('N',array('Doe', 'John'))); + $this->assertInstanceOf('File_IMC_Build_Vcard', $this->vcard->set('SOURCE','Your mom.')); } /** @@ -92,9 +275,8 @@ public function testFluentInterface() public function testFormattedName() { $name = 'Jane Doe'; - - $this->vcard->setFormattedName($name); - $this->assertSame("FN:{$name}", $this->vcard->getFormattedName()); + $this->vcard->set('FN',$name); + $this->assertSame("FN:{$name}", $this->vcard->get('FN')); } /** @@ -111,8 +293,9 @@ public function testVersionException() public function testVersion() { $version = '2.1'; - $this->vcard->setVersion($version); - - $this->assertSame("VERSION:{$version}", $this->vcard->getVersion()); + $this->vcard->set('VERSION',$version); + $this->assertSame("VERSION:{$version}", $this->vcard->get('VERSION')); } } + +?> \ No newline at end of file From e07f99fa222582738afaf642a974eb7e33b0546e Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:31:21 -0500 Subject: [PATCH 04/17] Update tests/File/IMC/ParseTest.php completed the testPropertyGroups and testParameters tests --- tests/File/IMC/ParseTest.php | 123 ++++++++++++++--------------------- 1 file changed, 50 insertions(+), 73 deletions(-) diff --git a/tests/File/IMC/ParseTest.php b/tests/File/IMC/ParseTest.php index d156d9e..d4ea7cd 100644 --- a/tests/File/IMC/ParseTest.php +++ b/tests/File/IMC/ParseTest.php @@ -73,8 +73,8 @@ public function setUp() $param1Name = "PARAM1"; $param1Value = "PARAMVALUE1"; - $param2Name = "PARAM2"; - $param2Value = "PARAMVALUE2"; + $param2Name = "X-PARAM"; + $param2Value = "X-VALUE"; $param3Value = "PARAMVALUE3"; $this->vcard = "BEGIN:VCARD\n\r" . @@ -88,6 +88,7 @@ public function setUp() $additionalNames . ";" . $prefix . ";" . $suffix . "\n\r" . + 'TZ;VALUE=text:-05:00; EST; Raleigh/North America'."\r\n" . "END:VCARD"; $this->parser = File_IMC::parse('vcard'); @@ -132,39 +133,21 @@ public static function getPropertyGroupVcard() $vcard .= "CATEGORIES:Customers:Verendus LLC" . "\n"; $vcard .= "X-ABUID:8291364B-FCBF-4577-8294-166AC0E8B9C7\:ABPerson" . "\n"; $vcard .= "END:VCARD"; - - return array( - array($vcard), - ); + return $vcard; } /** - * This test doesn't make any sense (yet). - * * @param string $vcard * @return void - * - * @dataProvider getPropertyGroupVcard */ - public function testPropertyGroups($vcard) + public function testPropertyGroups() { - $this->markTestIncomplete("Property groups are not yet implemented and this test didn't make any sense!"); - return; - - $vcard = $this->getPropertyGroupVcard(); - - list($ret) = $this->parser->fromText($vcard); - - list($data) = $ret["A.N"]; - $values = $data['value']; - - //var_dump($vcard, $values, $ret); - - $this->assertEquals("FamilyName", $values[0][0]); - $this->assertEquals("GivenName", $values[1][0]); - $this->assertEquals("Additional Names", $values[2][0]); - $this->assertEquals("Prefix.", $values[3][0]); - $this->assertEquals("Suffix", $values[4][0]); + $parsed = $this->parser->fromText($this->vcard); + $vcardData = $parsed['VCARD'][0]; + $nameValues = $vcardData['N'][0]['value']; + $this->assertEquals("FamilyName", $nameValues[0][0]); + $this->assertEquals("GivenName", $nameValues[1][0]); + $this->assertEquals("A", $vcardData['N'][0]['group']); } /** @@ -175,18 +158,15 @@ public function testPropertyGroups($vcard) */ public function testParameters() { - $this->markTestIncomplete("Not done yet!"); - - $ret = $this->parser->fromText($this->vcard); - //var_dump($ret); exit; - - list($data) = $ret["A.N"]; - - $expected = array("PARAM1" => array("PARAMVALUE1"), - "PARAM2" => array("PARAMVALUE2"), - 'TYPE' => array("PARAMVALUE3")); - - $this->assertSame($expected, $data['param']); + $parsed = $this->parser->fromText($this->vcard); + $vcardData = $parsed['VCARD'][0]; + + $expected = array( + 'PARAM1' => array('PARAMVALUE1'), + 'X-PARAM' => array('X-VALUE'), + 'PARAMVALUE3' => array('PARAMVALUE3'), + ); + $this->assertSame($expected, $vcardData['N'][0]['param']); } /** @@ -194,44 +174,41 @@ public function testParameters() * * @return array */ - public static function exampleProvider() + public function exampleProvider() { $data = self::getExampleVcard(); $parser = File_IMC::parse('vcard'); $parsed = $parser->fromText($data); - - $vcard = $parsed['VCARD'][0]; - + $vcardData = $parsed['VCARD'][0]; $data = array( - - array('3.0', $vcard['VERSION'][0]['value'][0][0]), - array('Shagnasty', $vcard['N'][0]['value'][0][0]), - array('Bolivar', $vcard['N'][0]['value'][1][0]), - array('Odysseus', $vcard['N'][0]['value'][2][0]), - array('Mr.', $vcard['N'][0]['value'][3][0]), - array('III', $vcard['N'][0]['value'][4][0]), - array('B.S.', $vcard['N'][0]['value'][4][1]), - array('Bolivar Shagnasty', $vcard['FN'][0]['value'][0][0]), - - // Address - array('HOME', $vcard['ADR'][0]['param']['TYPE'][0]), - array('WORK', $vcard['ADR'][0]['param']['TYPE'][1]), - array('123 Main', $vcard['ADR'][0]['value'][2][0]), - array('Apartment 101', $vcard['ADR'][0]['value'][2][1]), - array('Beverly Hills', $vcard['ADR'][0]['value'][3][0]), - array('CA', $vcard['ADR'][0]['value'][4][0]), - array('90210', $vcard['ADR'][0]['value'][5][0]), - array('', $vcard['ADR'][0]['value'][6][0]), - - // Email - array('HOME', $vcard['EMAIL'][0]['param']['TYPE'][0]), - array('WORK', $vcard['EMAIL'][0]['param']['TYPE'][1]), - array('boshag@example.com', $vcard['EMAIL'][0]['value'][0][0]), - array('PREF', $vcard['EMAIL'][1]['param']['TYPE'][0]), - array('boshag@ciaweb.net', $vcard['EMAIL'][1]['value'][0][0]), + // VERSION + array('3.0', $vcardData['VERSION'][0]['value'][0][0]), + // N + array('Shagnasty', $vcardData['N'][0]['value'][0][0]), + array('Bolivar', $vcardData['N'][0]['value'][1][0]), + array('Odysseus', $vcardData['N'][0]['value'][2][0]), + array('Mr.', $vcardData['N'][0]['value'][3][0]), + array('III', $vcardData['N'][0]['value'][4][0]), + array('B.S.', $vcardData['N'][0]['value'][4][1]), + // FN + array('Bolivar Shagnasty', $vcardData['FN'][0]['value'][0][0]), + // ADR + array('HOME', $vcardData['ADR'][0]['param']['TYPE'][0]), + array('WORK', $vcardData['ADR'][0]['param']['TYPE'][1]), + array('123 Main', $vcardData['ADR'][0]['value'][2][0]), + array('Apartment 101', $vcardData['ADR'][0]['value'][2][1]), + array('Beverly Hills', $vcardData['ADR'][0]['value'][3][0]), + array('CA', $vcardData['ADR'][0]['value'][4][0]), + array('90210', $vcardData['ADR'][0]['value'][5][0]), + array('', $vcardData['ADR'][0]['value'][6][0]), + // EMAIL + array('HOME', $vcardData['EMAIL'][0]['param']['TYPE'][0]), + array('WORK', $vcardData['EMAIL'][0]['param']['TYPE'][1]), + array('boshag@example.com', $vcardData['EMAIL'][0]['value'][0][0]), + array('PREF', $vcardData['EMAIL'][1]['param']['TYPE'][0]), + array('boshag@ciaweb.net', $vcardData['EMAIL'][1]['value'][0][0]), ); - return $data; } @@ -248,8 +225,6 @@ public static function exampleProvider() */ public function testExampleParser($expect, $actual) { - //$this->markTestIncomplete("Not done yet!"). - $this->assertSame($expect, $actual); } @@ -318,3 +293,5 @@ public function testExceptionIfInvalidFormatIsProvided() $foo = File_IMC::parse('bar'); } } + +?> \ No newline at end of file From b6f9b42fc9250c85ad68925ac303e7293fdd50b0 Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:35:28 -0500 Subject: [PATCH 05/17] Update docs/vcalendar_parse_example.php --- docs/vcalendar_parse_example.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/vcalendar_parse_example.php b/docs/vcalendar_parse_example.php index 721b43c..dc4e356 100644 --- a/docs/vcalendar_parse_example.php +++ b/docs/vcalendar_parse_example.php @@ -2,12 +2,12 @@ require_once 'File/IMC.php'; -$parse = File_IMC::parse('vCalendar'); +$parser = File_IMC::parse('vCalendar'); // parse a vCalendar file and store the data // in $calinfo -$calinfo = $parse->fromFile('sample.vcs'); - +$calinfo = $parser->fromFile('sample.vcs'); + // view the calendar info array echo '
';
 print_r($calinfo);

From f12f81be494c2dd389a330aa5d7291988518bf64 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Fri, 17 Aug 2012 13:36:29 -0500
Subject: [PATCH 06/17] Update docs/vcard_build_example.php

now uses the set() method
---
 docs/vcard_build_example.php | 46 +++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/docs/vcard_build_example.php b/docs/vcard_build_example.php
index 4ddc8ff..e5f0e1d 100644
--- a/docs/vcard_build_example.php
+++ b/docs/vcard_build_example.php
@@ -17,42 +17,60 @@
 // |          Marshall Roch                                |
 // +----------------------------------------------------------------------+
 //
-// $Id$
+// $Id: vcard_build_example.php 283068 2009-06-29 19:54:28Z till $
 
 // include the class file
 require_once 'File/IMC.php';
 
 // instantiate a builder object
 // (defaults to version 3.0)
-$vcard = File_IMC::build('vCard');
+$builder = File_IMC::build('vCard');
 
 // set a formatted name
-$vcard->setFormattedName('Bolivar Shagnasty');
+$builder->set('FN','Bolivar Shagnasty');
 
 // set the structured name parts
-$vcard->setName('Shagnasty', 'Bolivar', 'Odysseus', 'Mr.', 'III');
+$builder->set('N',array(
+                   'Shagnasty',
+	'Bolivar',
+	'Odysseus',
+	'Mr.',
+	'III'
+));
 
 // add a work email.  note that we add the value
 // first and the param after -- Contact_Vcard_Build
 // is smart enough to add the param in the correct
 // place.
-$vcard->addEmail('boshag@example.com');
-$vcard->addParam('TYPE', 'WORK');
+$builder->set('EMAIL','boshag@example.com');
+$builder->addParam('TYPE', 'WORK');
 
 // add a home/preferred email
-$vcard->addEmail('bolivar@example.net');
-$vcard->addParam('TYPE', 'HOME');
-$vcard->addParam('TYPE', 'PREF');
+//	if we didn't specify the 3rd parameter, it
+//	would default to 0 (the first email)..
+//	and overwrite the email set above
+//	could also pass the integer 1 to
+//	explicitly specify the index to set
+$builder->set('EMAIL','bolivar@example.net','new');
+$builder->addParam('TYPE', 'HOME');
+$builder->addParam('TYPE', 'PREF');
 
 // add a work address
-$vcard->addAddress('POB 101', 'Suite 202', '123 Main',
-                   'Beverly Hills', 'CA', '90210', 'US');
-$vcard->addParam('TYPE', 'WORK');
+$builder->set('ADR',array(
+	'POB 101',
+	'Suite 202',
+	'123 Main',
+	'Beverly Hills',
+	'CA',
+	'90210',
+	'US'
+));
+$builder->addParam('TYPE', 'WORK');
 
 // get back the vCard and print it
-$text = $vcard->fetch();
+$text = $builder->fetch();
 echo '
';
 print_r($text);
 echo '
'; - + ?> From 73c36c7d1421c7f53845263c1bae0457d0ef3361 Mon Sep 17 00:00:00 2001 From: Brad Kent Date: Fri, 17 Aug 2012 13:37:03 -0500 Subject: [PATCH 07/17] Update docs/vcard_parse_example.php --- docs/vcard_parse_example.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/vcard_parse_example.php b/docs/vcard_parse_example.php index 7a19a05..adff79c 100644 --- a/docs/vcard_parse_example.php +++ b/docs/vcard_parse_example.php @@ -17,15 +17,15 @@ // | Marshall Roch | // +----------------------------------------------------------------------+ // -// $Id$ +// $Id: vcard_parse_example.php 283068 2009-06-29 19:54:28Z till $ require_once 'File/IMC.php'; // create vCard parser -$parse = File_IMC::parse('vCard'); +$parser = File_IMC::parse('vCard'); // parse a vCard file and store the data in $cardinfo -$cardinfo = $parse->fromFile('sample.vcf'); +$cardinfo = $parser->fromFile('sample.vcf'); // view the card info array echo '
';

From 39a3b1d4f1594dd2c138e1d64fbc7294208874df Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Fri, 17 Aug 2012 13:44:07 -0500
Subject: [PATCH 08/17] Update docs/contact_vcard_build.html

not sure where this is seen/used... updated examples to use the new set() method
---
 docs/contact_vcard_build.html | 48 ++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 18 deletions(-)

diff --git a/docs/contact_vcard_build.html b/docs/contact_vcard_build.html
index 83eb36e..e393521 100644
--- a/docs/contact_vcard_build.html
+++ b/docs/contact_vcard_build.html
@@ -33,38 +33,50 @@ 

Quick Instructions

<?php
     
     // include the class file
-    require_once 'Contact_Vcard_Build.php';
+    require_once 'File/IMC.php';
     
     // instantiate a builder object
     // (defaults to version 3.0)
-    $vcard = new Contact_Vcard_Build();
+    $builder = File_IMC::build('vCard');
     
     // set a formatted name
-    $vcard->setFormattedName('Bolivar Shagnasty');
+    $builder->set('FN','Bolivar Shagnasty');
     
     // set the structured name parts
-    $vcard->setName('Shagnasty', 'Bolivar', 'Odysseus',
-        'Mr.', 'III');
+    $builder->set('N',array(
+    	'Shagnasty',
+    	'Bolivar',
+    	'Odysseus',
+        'Mr.',
+        'III'
+    ));
     
     // add a work email.  note that we add the value
     // first and the param after -- Contact_Vcard_Build
     // is smart enough to add the param in the correct
     // place.
-    $vcard->addEmail('boshag@example.com');
-    $vcard->addParam('TYPE', 'WORK');
+    $builder->set('EMAIL','boshag@example.com');
+    $builder->addParam('TYPE', 'WORK');
     
     // add a home/preferred email
-    $vcard->addEmail('bolivar@example.net');
-    $vcard->addParam('TYPE', 'HOME');
-    $vcard->addParam('TYPE', 'PREF');
+    $builder->set('EMAIL','bolivar@example.net','new');
+    $builder->addParam('TYPE', 'HOME');
+    $builder->addParam('TYPE', 'PREF');
     
     // add a work address
-    $vcard->addAddress('POB 101', 'Suite 202', '123 Main',
-        'Beverly Hills', 'CA', '90210', 'US');
-    $vcard->addParam('TYPE', 'WORK');
+    $builder->set('ADR',array(
+    	'POB 101',
+    	'Suite 202',
+    	'123 Main',
+        'Beverly Hills',
+        'CA',
+        '90210',
+        'US'
+    ));
+    $builder->addParam('TYPE', 'WORK');
     
     // get back the vCard and print it
-    $text = $vcard->fetch();
+    $text = $builder->fetch();
     echo '<pre>';
     print_r($text);
     echo '</pre>';
@@ -308,16 +320,16 @@ 

How To Set A Parameter Value

     // add first address iteration
-    $vcard->addAddress($pobox0, $extend0, $street0, $city0, 
-        $state0, $zip0, $country0);
+    $vcard->set('ADR',array('$pobox0, $extend0, $street0, $city0, 
+        $state0, $zip0, $country0));
     
     // add parameters to the first address
     $vcard->addParam('TYPE', 'HOME');
     $vcard->addParam('TYPE', 'PREF');
     
     // add second address iteration
-    $vcard->addAddress($pobox1, $extend1, $street1, $city1, 
-        $state1, $zip1, $country1);
+    $vcard->set('ADR',array('$pobox1, $extend1, $street1, $city1, 
+        $state1, $zip1, $country1),1);	// could also pass "new" as 3rd parameter
     
     // add parameters to the second address
     $vcard->addParam('TYPE', 'WORK');

From 410e4d86e4b0c6f3512817d28e065ee599b26d0b Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Fri, 17 Aug 2012 21:12:17 -0500
Subject: [PATCH 09/17] imaged used to test setting PHOTO

---
 tests/test_pattern.gif | Bin 0 -> 1668 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 tests/test_pattern.gif

diff --git a/tests/test_pattern.gif b/tests/test_pattern.gif
new file mode 100644
index 0000000000000000000000000000000000000000..42bb3d19d014f0bc528f1347d5e8867d6af250b5
GIT binary patch
literal 1668
zcmV-~27CEONk%w1VW0qb0K@%
zb)8|KphBXfrEI6Ds*0>%u41u~vuU-Kw+uS^!4`g6CC>c{QdqM83GC%NU&f)3d*|ScoYF*2=
ztx2zNg%TZ0H)&b4c%iCw%eOB~op7+Nnt4N=9+pGgDG@geun2R=2p2Z&1F5VB~nNXq@CTnW0$EJ;E
zRySpwJkF^YlD43!Bc4R+=?o##l$HwlDXO8)GU{f2x{7I+ny%_*txLY@NUWmF3Pq`!`05F;
zle{-wd~MA~pM8?$=hl9Jy~iy7Th77;En(8OMXgR7ge&g2`7M=e0e9M&%ewjhDV=9?d7z;OJNhN14|aN_sVB=g;f}u!YV2#TZb<8&J>HGl
zw9HO)N&QUsI23_gsUSJ)GOe
zP5#))OMj{OVXwb<`;FTTKi=}|Ej;JMLu7pZg4-YI`iGAHS>}3;Ip3qw2RZ;sPJpZ<
zpf(JM7z6@Gfu3R@;(+%&-MxW#&GB6`I4BGV?oNWrpdd>us2mKQO@okgpyo{YK@{#n
zh0kE&d0gnX`vFddqN5=u?B~Dg9ngE`1E2PMh&dptPKZ|k;dNm6JLi>rE$n^)0hGIiNa1A!An;#4Ncg66VwHVv8T+$GS0
z_Dy$g1)lL-<~(m!PkXu~o%t*$Ui#_JnBg;?0+mof{Q^*g?o(+CwdFz8nb3
Date: Mon, 20 Aug 2012 19:48:22 -0500
Subject: [PATCH 10/17] Update docs/vcard_build_example.php

---
 docs/vcard_build_example.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/vcard_build_example.php b/docs/vcard_build_example.php
index e5f0e1d..41593bc 100644
--- a/docs/vcard_build_example.php
+++ b/docs/vcard_build_example.php
@@ -17,7 +17,7 @@
 // |          Marshall Roch                                |
 // +----------------------------------------------------------------------+
 //
-// $Id: vcard_build_example.php 283068 2009-06-29 19:54:28Z till $
+// $Id$
 
 // include the class file
 require_once 'File/IMC.php';

From 4eabd3f8fde2348b1be6114e49dd5c7e454218c7 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Mon, 20 Aug 2012 19:49:04 -0500
Subject: [PATCH 11/17] Update docs/vcard_parse_example.php

---
 docs/vcard_parse_example.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/vcard_parse_example.php b/docs/vcard_parse_example.php
index adff79c..7d3a746 100644
--- a/docs/vcard_parse_example.php
+++ b/docs/vcard_parse_example.php
@@ -17,7 +17,7 @@
 // |          Marshall Roch                                |
 // +----------------------------------------------------------------------+
 //
-// $Id: vcard_parse_example.php 283068 2009-06-29 19:54:28Z till $
+// $Id$
 
 require_once 'File/IMC.php';
 

From 7a28607e7a91e817a9986329420be459bc3fc3f3 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Thu, 4 Apr 2013 16:00:26 -0500
Subject: [PATCH 12/17] Update Build.php

after running phpCS
---
 File/IMC/Build.php | 1280 ++++++++++++++++++++++----------------------
 1 file changed, 642 insertions(+), 638 deletions(-)

diff --git a/File/IMC/Build.php b/File/IMC/Build.php
index fa1c506..29b4832 100644
--- a/File/IMC/Build.php
+++ b/File/IMC/Build.php
@@ -1,7 +1,7 @@
 
  * @license  http://www.opensource.org/licenses/bsd-license.php The BSD License
  * @version  SVN: $Id$
- * @link	 http://pear.php.net/package/File_IMC
+ * @link     http://pear.php.net/package/File_IMC
  */
 
 /**
@@ -64,645 +64,649 @@
 * @author   Till Klampaeckel 
 * @license  http://www.opensource.org/licenses/bsd-license.php The BSD License
 * @version  Release: @package_version@
-* @link	 http://pear.php.net/package/File_IMC
+* @link     http://pear.php.net/package/File_IMC
 */
 abstract class File_IMC_Build
 {
-	/**
-	* Values for vCard properties
-	*
-	* @var array
-	*/
-	public $value = array();
-
-	/**
-	* Parameters for vCard properties
-	*
-	* @var array
-	*/
-	public $param = array();
-
-	/**
-	* Groups for vCard properties
-	*
-	* @var array
-	*/
-	public $group = array();
-
-	/**
-	* Tracks which property (N, ADR, TEL, etc) value was last set or added.
-	* Used so that property need not be specified when adding parameters or groups
-	*
-	* @access private
-	* @var string
-	*/
-	protected $lastProp = null;
-
-	/**
-	* Tracks which iteration was last used
-	* Used so that iteration need not be specified when adding parameters or groups
-	*
-	* @access private
-	* @var int
-	*/
-	protected $lastIter = null;
-
-	/**
-	* Sets the version of the specification to use.  Only one iteration.
-	* Overload this function in the driver to validate and set the version
-	*
-	* @param string $text The text value of the verson text (e.g. '3.0' or '2.1').
-	*
-	* @return mixed Void on success, or a PEAR_Error object on failure.
-	*/
-	abstract function setVersion($text = '3.0');
-
-	/**
-	* Validates parameter names and values
-	*
-	* @param string $name The parameter name (e.g., TYPE or ENCODING).
-	*
-	* @param string $text The parameter value (e.g., HOME or BASE64).
-	*
-	* @param string $prop Optional, the property name (e.g., ADR or
-	* PHOTO). Only used for error messaging.
-	*
-	* @param string $iter Optional, the iteration of the property. Only
-	* used for error messaging.
-	*
-	* @return void
-	* @throws File_IMC_Exception if not.
-	*/
-	abstract function validateParam($name, $text, $prop = null, $iter = null);
-
-	/**
-	* Fetches a full vCard/vCal text block based on $this->value and
-	* $this->param.
-	*
-	* @return string A properly formatted vCard/vCalendar text block.
-	*/
-	abstract function fetch();
-
-	/**
-	* @access private
-	* @param string property
-	* @param in iteration
-	*/
-	protected function _setLast($prop,$iter) {
-		$this->lastProp = $prop;
-		$this->lastIter = $iter;
-	}
-
-	/**
-	*
-	* Resets the vCard values and params to be blank.
-	*
-	* @param string $version The vCard version to reset to ('2.1' or
-	* '3.0' -- default is the same version as previously set).
-	*
-	* @return void
-	*/
-	public function reset($version = null)
-	{
-		if ( $version === null && isset($this->value['VERSION']) )
-			$version = $this->value['VERSION'][0][0][0];
-		$this->value = array();
-		$this->param = array();
-		$this->group = array();
-		$this->lastProp = null;
-		$this->lastIter = null;
-		$this->setVersion($version);	// setVersion will set to default ver if null
-	}
-
-	/**
-	*
-	* Gets back the version of the the vCard.  Only one iteration.
-	*
-	* @return string The data-source of the vCard.
-	*
-	* @access public
-	*/
-	public function getVersion()
-	{
-		return $this->getMeta('VERSION', 0) . $this->getValue('VERSION', 0);
-	}
-
-	/**
-	* Check if encoding parameter has been set for this property/iteration
-	*   If so... it is assumed that the value has already been encoded as such
-	*   Otherwide, encode the value if necessary and sets the encoding parameter
-	*
-	* @param string $prop
-	* @param int $iter
-	* @return void
-	*/
-	public function encode($prop,$iter)
-	{
-		$ver = $this->value['VERSION'][0][0][0];
-		if ( $ver == '2.1' )
-		{
-			if ( empty($this->param[$prop][$iter]['ENCODING']) )
-			{
-				foreach ( $this->value[$prop][$iter] as $part => $a )
-				{
-					foreach ( $a as $rept => $val )
-					{
-						$val_new = quoted_printable_encode($val);
-						$val_new = str_replace("=\r\n",'',$val_new);	// quoted_printable_encode wrapped/folded the text... undo
-																		//  lines will get folded via fetch()
-						$val_new = str_replace(array("\r","\n"),array('=0D','=0A'),$val_new);
-						if ( $val_new != $val )
-						{
-							$this->addParam('ENCODING', 'QUOTED-PRINTABLE', $prop, $iter);
-							$this->value[$prop][$iter][$part][$rept] = $val_new;
-						}
-					}
-				}
-			}
-		}
-		return;
-	}
-
-	/**
-	* Prepares a string so it may be safely used as vCard values.  DO
-	* NOT use this with binary encodings.  Operates on text in-place;
-	* does not return a value.  Recursively descends into arrays.
-	*
-	* Escapes a string so that...
-	*	 ; => \;
-	*	 , => \,
-	*	 newline => literal \n
-	*
-	* @param mixed $text The string or array or strings to escape.
-	*
-	* @return void
-	* @throws File_IMC_Exception on failure.
-	*/
-	public function escape(&$text)
-	{
-		if (is_object($text)) {
-			throw new File_IMC_Exception(
-				'The escape() method works only with string literals and arrays.',
-				FILE_IMC::ERROR_INVALID_PARAM_TYPE);
-		}
-		if (is_array($text)) {
-			// the "text" is really an array; recursively descend into
-			// the array and escape text as we go, then set the value
-			// of the current "text" (which is really an array).
-			foreach ($text as $key => $val) {
-				$this->escape($val);
-				$text[$key] = $val;
-			}
-			return;
-		}
-		//$regex = '(?value['VERSION'][0][0][0];
-		$regex = $ver == '3.0'
-			? '(?addParam('TYPE', 'HOME', 'TEL', 0);
-	* $vcard->addParam('TYPE', 'PREF', 'TEL', 0);
-	*
-	* @param string $param_name The parameter name, such as TYPE, VALUE,
-	* or ENCODING.
-	*
-	* @param string $param_value The parameter value.
-	*
-	* @param string $prop The vCard property for which this is a
-	* paramter (ADR, TEL, etc).  If null, will be the property that was
-	* last set or added-to.
-	*
-	* @param mixed $iter An integer vCard property iteration that this
-	* is a param for.  E.g., if you have more than one ADR property, 0
-	* refers to the first ADR, 1 to the second ADR, and so on.  If null,
-	* the parameter will be added to the last property iteration
-	* available.
-	*
-	* @return void
-	* @throws File_IMC_Excpetion on failure.
-	*/
-	public function addParam($param_name, $param_value, $prop=null, $iter=null)
-	{
-		// if property is not specified, default to the last property that was set or added.
-		if ($prop === null) {
-			$prop = $this->lastProp;
-		}
-		// if property is not specified, default to the last iteration that was set or added.
-		if ($iter === null) {
-			//$iter = count($this->value[$prop]) - 1;
-			$iter = $this->lastIter;
-		}
-
-		// massage the text arguments
-		$prop			= strtoupper(trim($prop));
-		$param_name		= strtoupper(trim($param_name));
-		$param_value	= trim($param_value);
-
-		if ( !is_integer($iter) || $iter < 0) {
-			throw new File_IMC_Exception($iter.' is not a valid iteration number for '.$prop.'; must be a positive integer.',
-				FILE_IMC::ERROR_INVALID_ITERATION);
-		}
-
-		try {
-			$result = $this->validateParam($param_name, $param_value, $prop, $iter);
-			if ( $result )
-				$this->param[$prop][$iter][$param_name][] = $param_value;
-		} catch (File_IMC_Exception $e) {
-			throw $e; // FIXME: check later
-		}
-
-	}
-
-	/**
-	* Gets back the group for a given property.
-	*
-	* @param string $prop The property to get parameters for (ADR, TEL, etc).
-	*
-	* @param int $iter The vCard property iteration to get the param
-	* list for. E.g., if you have more than one ADR property, 0 refers
-	* to the first ADR, 1 to the second ADR, and so on.
-	*
-	* @return string
-	*/
-	public function getGroup($prop=null, $iter=null)
-	{
-		$prop = $prop == null
-			? $this->lastProp
-			: trim(strtoupper($prop));
-		$iter = $iter == null
-			? $this->lastIter
-			: $iter;
-		$text = isset($this->group[$prop][$iter])
-			? $this->group[$prop][$iter]
-			: '';
-		return $text;
-	}
-
-	/**
-	* Sets the group for a given property.
-	*
-	* @param string $groupNAme The group to assign to the property
-	*
-	* @param string $prop The property (ADR, TEL, etc).
-	*  If null, will be the property that was last set or added-to.
-	*
-	* @param int $iter An integer vCard property iteration that this is a param for
-	*  If null, will be the iteration that was last set or added-to.
-	*
-	* @return void
-	*/
-	public function setGroup($groupName, $prop=null, $iter=null)
-	{
-		$prop = $prop === null
-			? $this->lastProp
-			: trim(strtoupper($prop));
-		$iter = $iter === null
-			? $this->lastIter
-			: $iter;
-		$this->group[$prop][$iter] = $groupName;
-		$this->_setLast($prop,$iter);
-	}
-
-	/**
-	* Gets the left-side/prefix/before-the-colon (metadata) part of a
-	* vCard line, including the property identifier, the parameter
-	* list, and a colon.
-	*
-	* @param string $prop The property to get metadata for (ADR, TEL, etc).
-	*
-	* @param int $iter The vCard property iteration to get the metadata
-	* for. E.g., if you have more than one ADR property, 0 refers to
-	* the first ADR, 1 to the second ADR, and so on.
-	*
-	* @return string The line prefix metadata.
-	*/
-	public function getMeta($prop, $iter = 0)
-	{
-		$text = '';
-		$group	= $this->getGroup($prop, $iter);
-		$params	= $this->getParam($prop, $iter);
-		if ( !empty($group) )
-			$text .= $group.'.';
-		$text .= $prop;
-		if ( trim($params) != '' )
-			$text .= ';'.$params;
-		$text .= ':';
-		return $text;
-	}
-
-	/**
-	* Generic, all-purpose method to store a string or array in
-	* $this->value, in a way suitable for later output as a vCard
-	* element.  This forces the value to be the passed text or array
-	* value, overriding any prior values.
-	*
-	* @param string $prop The property to set the value for ('N','ADR', etc).
-	*
-	* @param int $iter The property-iteration to set the value for.
-	*
-	* @param int $part The part number of the property-iteration to set
-	* the value for.
-	*
-	* @param mixed $value A string or array; the set of repeated values
-	* for this property-iteration part.
-	*
-	* @return void
-	*/
-	public function setValue($prop, $iter, $part, $value)
-	{
-		$prop = strtoupper($prop);
-		settype($value, 'array');
-		if ( !isset($this->value[$prop]) ) {
-			$this->value[$prop] = array();
-		}
-		if ( !isset($this->value[$prop][$iter]) ) {
-			$this->value[$prop][$iter] = array();
-		}
-		$this->value[$prop][$iter][$part] = $value;
-		$this->_setLast($prop,$iter);
-	}
-
-	/**
-	* Generic, all-purpose method to add a repetition of a string or
-	* array in $this->value, in a way suitable for later output as a
-	* vCard element.  This appends the value to be the passed text or
-	* array value, leaving any prior values in place.
-	*
-	* @param string $prop The property to set the value for ('N',
-	* 'ADR', etc).
-	*
-	* @param int $iter The property-iteration to set the value for.
-	*
-	* @param int $part The part number of the property-iteration to set
-	* the value for.
-	*
-	* @param mixed $value A string or array; the set of repeated values
-	* for this property-iteration part.
-	*
-	* @return void
-	*/
-	public function addValue($prop, $iter, $part, $vals)
-	{
-		$prop = strtoupper($prop);
-		settype($vals, 'array');
-		foreach ($vals as $val) {
-			$this->value[$prop][$iter][$part][] = $val;
-		}
-		$this->_setLast($prop,$iter);
-	}
-
-	/**
-	* Generic, all-purpose method to get back the data stored in $this->value.
-	*
-	* @param string $prop The property to set the value for ('N','ADR', etc).
-	*
-	* @param int $iter The property-iteration to set the value for.
-	*
-	* @param int $part The part number of the property-iteration to get
-	* the value for.
-	*
-	* @param mixed $rept The repetition number within the part to get;
-	* if null, get all repetitions of the part within the iteration.
-	*
-	* @return string The value, escaped and delimited, of all
-	* repetitions in the property-iteration part (or specific
-	* repetition within the part).
-	*/
-	public function getValue($prop, $iter=0, $part=0, $rept=null)
-	{
-		if ( $rept === null && is_array($this->value[$prop][$iter][$part]) ) {
-			// get all repetitions of a part
-			$list = array();
-			foreach ($this->value[$prop][$iter][$part] as $key => $val) {
-				$list[] = trim($val);
-			}
-			$this->escape($list);
-			return implode(',', $list);
-		}
-		else {
-			// get a specific repetition of a part
-			$value = trim($this->value[$prop][$iter][$part][$rept]);
-			$this->escape($value);
-			return $value;
-		}
-	}
-
-	/**
-	* Gets back the parameter string for a given property.
-	*
-	* @param string $prop The property to get parameters for (ADR, TEL,
-	* etc).
-	*
-	* @param int $iter The vCard property iteration to get the param
-	* list for. E.g., if you have more than one ADR property, 0 refers
-	* to the first ADR, 1 to the second ADR, and so on.
-	*
-	* @return string
-	*/
-	public function getParam($prop, $iter = 0)
-	{
-		$prop = trim(strtoupper($prop));
-		$text = '';
-
-		if (!isset($this->param[$prop])) {
-			$this->param[$prop] = array();
-		}
-		if (!isset($this->param[$prop][$iter])
-			|| !is_array($this->param[$prop][$iter])) {
-			// if there were no parameters, this will be blank.
-			return $text;
-		}
-
-		// loop through the array of parameters for
-		// the property
-
-		foreach ($this->param[$prop][$iter] as $param_name => $param_val) {
-
-			// if there were previous parameter names, separate with
-			// a semicolon
-			if ($text != '') {
-				$text .= ';';
-			}
-
-			if ($param_val === null) {
-
-				// no parameter value was specified, which is typical
-				// for vCard version 2.1 -- the name is the value.
-				$this->escape($param_name);
-				$text .= $param_name;
-
-			} else {
-				// set the parameter name...
-				$text .= strtoupper($param_name) . '=';
-
-				// ...then escape and comma-separate the parameter
-				// values.
-				$this->escape($param_val);
-				$text .= implode(',', $param_val);
-			}
-		}
-		// if there were no parameters, this will be blank.
-		return $text;
-	}
-
-	/**
-	* Builds a vCard/vCal from a parser result array.  Only send
-	* one vCard from the parse-results.
-	*
-	* Usage (to build from first vCard in parsed results):
-	*
-	* $parse = File_IMC::parse('vCard'); // new parser
-	* $info = $parse->fromFile('sample.vcf'); // parse file
-	*
-	* $vcard = File_IMC::build('vCard'); // new builder
-	* $vcard->setFromArray($info);
-	*
-	* @param  array  $src One vCard entry as parsed using File_IMC::parse()
-	*
-	* @return void
-	*
-	* @see File_IMC_Parse::fromFile()
-	* @see File_IMC_Parse::fromText()
-	*/
-	public function setFromArray(array $src)
-	{
-		// reset to a blank values and params
-		$this->value = array();
-		$this->param = array();
-		$this->group = array();
-		foreach ($src as $card => $card_val) {
-			// loop through properties (N, ADR, TEL, etc)
-			foreach ($card_val AS $prop => $prop_val) {
-				$prop = strtoupper($prop);
-				$this->lastProp = $prop;
-				// iteration number of each property
-				foreach ($prop_val AS $iter => $iter_val) {
-					$this->lastIter = $iter;
-					foreach ($iter_val AS $kind => $kind_val) {
-						$kind = strtolower($kind);
-						if ( $kind == 'group' ) {
-							$this->group[$prop][$iter] = $kind_val;
-						}
-						elseif ( is_array($kind_val) ) {
-							foreach ( $kind_val AS $part => $part_val ) {
-								foreach ( $part_val AS $rept => $text ) {
-									if ( $kind == 'value' ) {
-										$this->value[$prop][$iter][$part][$rept] = $text;
-									} elseif ( $kind == 'param' ) {
-										$this->param[$prop][$iter][$part][$rept] = $text;
-									} else {
-										// ignore data when $kind is neither 'value' nor 'param'
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * Magic method to display the vCard/vCal.
-	 *
-	 * 
-	 *
-	 * $vcard = File_IMC::build('vCard');
-	 *
-	 * // set "TYPE=HOME,PREF" for the first TEL property
-	 * $vcard->addParam('TYPE', 'HOME', 'TEL', 0);
-	 * $vcard->addParam('TYPE', 'PREF', 'TEL', 0);
-	 *
-	 * echo $vcard;
-	 *
-	 * 
-	 *
-	 * @return string
-	 * @uses   self::fetch()
-	 */
-	public function __toString()
-	{
-		return $this->fetch();
-	}
+    /**
+    * Values for vCard properties
+    *
+    * @var array
+    */
+    public $value = array();
+
+    /**
+    * Parameters for vCard properties
+    *
+    * @var array
+    */
+    public $param = array();
+
+    /**
+    * Groups for vCard properties
+    *
+    * @var array
+    */
+    public $group = array();
+
+    /**
+    * Tracks which property (N, ADR, TEL, etc) value was last set or added.
+    * Used so that property need not be specified when adding parameters or groups
+    *
+    * @access private
+    * @var string
+    */
+    protected $lastProp = null;
+
+    /**
+    * Tracks which iteration was last used
+    * Used so that iteration need not be specified when adding parameters or groups
+    *
+    * @access private
+    * @var int
+    */
+    protected $lastIter = null;
+
+    /**
+    * Sets the version of the specification to use.  Only one iteration.
+    * Overload this function in the driver to validate and set the version
+    *
+    * @param string $text The text value of the verson text (e.g. '3.0' or '2.1').
+    *
+    * @return mixed Void on success, or a PEAR_Error object on failure.
+    */
+    abstract function setVersion($text = '3.0');
+
+    /**
+    * Validates parameter names and values
+    *
+    * @param string $name The parameter name (e.g., TYPE or ENCODING).
+    *
+    * @param string $text The parameter value (e.g., HOME or BASE64).
+    *
+    * @param string $prop Optional, the property name (e.g., ADR or
+    * PHOTO). Only used for error messaging.
+    *
+    * @param string $iter Optional, the iteration of the property. Only
+    * used for error messaging.
+    *
+    * @return void
+    * @throws File_IMC_Exception if not.
+    */
+    abstract function validateParam($name, $text, $prop = null, $iter = null);
+
+    /**
+    * Fetches a full vCard/vCal text block based on $this->value and
+    * $this->param.
+    *
+    * @return string A properly formatted vCard/vCalendar text block.
+    */
+    abstract function fetch();
+
+    /**
+    * _setLast
+    *
+    * @param string $prop property
+    * @param int    $iter iteration
+    *
+    * @return void
+    * @access private
+    */
+    protected function _setLast($prop,$iter)
+    {
+        $this->lastProp = $prop;
+        $this->lastIter = $iter;
+    }
+
+    /**
+    * Resets the vCard values and params to be blank.
+    *
+    * @param string $version The vCard version to reset to ('2.1' or
+    * '3.0' -- default is the same version as previously set).
+    *
+    * @return void
+    */
+    public function reset($version = null)
+    {
+        if ( $version === null && isset($this->value['VERSION']) ) {
+            $version = $this->value['VERSION'][0][0][0];
+        }
+        $this->value = array();
+        $this->param = array();
+        $this->group = array();
+        $this->lastProp = null;
+        $this->lastIter = null;
+        $this->setVersion($version);	// setVersion will set to default ver if null
+    }
+
+    /**
+    * Gets back the version of the the vCard.  Only one iteration.
+    *
+    * @return string The data-source of the vCard.
+    *
+    * @access public
+    */
+    public function getVersion()
+    {
+        return $this->getMeta('VERSION', 0) . $this->getValue('VERSION', 0);
+    }
+
+    /**
+    * Check if encoding parameter has been set for this property/iteration
+    *   If so... it is assumed that the value has already been encoded as such
+    *   Otherwide, encode the value if necessary and sets the encoding parameter
+    *
+    * @param string $prop property
+    * @param int    $iter iteration
+    *
+    * @return void
+    */
+    public function encode($prop,$iter)
+    {
+        $ver = $this->value['VERSION'][0][0][0];
+        if ( $ver == '2.1' ) {
+            if ( empty($this->param[$prop][$iter]['ENCODING']) ) {
+                foreach ( $this->value[$prop][$iter] as $part => $a ) {
+                    foreach ( $a as $rept => $val ) {
+                        $val_new = quoted_printable_encode($val);
+                        // quoted_printable_encode wrapped/folded the text... undo
+                        //  lines will get folded via fetch()
+                        $val_new = str_replace("=\r\n", '', $val_new);
+                        $val_new = str_replace(array("\r","\n"), array('=0D','=0A'), $val_new);
+                        if ( $val_new != $val ) {
+                            $this->addParam('ENCODING', 'QUOTED-PRINTABLE', $prop, $iter);
+                            $this->value[$prop][$iter][$part][$rept] = $val_new;
+                        }
+                    }
+                }
+            }
+        }
+        return;
+    }
+
+    /**
+    * Prepares a string so it may be safely used as vCard values.  DO
+    * NOT use this with binary encodings.  Operates on text in-place;
+    * does not return a value.  Recursively descends into arrays.
+    *
+    * Escapes a string so that...
+    *	 ; => \;
+    *	 , => \,
+    *	 newline => literal \n
+    *
+    * @param mixed &$text The string or array or strings to escape.
+    *
+    * @return void
+    * @throws File_IMC_Exception on failure.
+    */
+    public function escape(&$text)
+    {
+        if (is_object($text)) {
+            throw new File_IMC_Exception(
+                'The escape() method works only with string literals and arrays.',
+                FILE_IMC::ERROR_INVALID_PARAM_TYPE
+            );
+        }
+        if (is_array($text)) {
+            // the "text" is really an array; recursively descend into
+            // the array and escape text as we go, then set the value
+            // of the current "text" (which is really an array).
+            foreach ($text as $key => $val) {
+                $this->escape($val);
+                $text[$key] = $val;
+            }
+            return;
+        }
+        //$regex = '(?value['VERSION'][0][0][0];
+        $regex = $ver == '3.0'
+            ? '(?addParam('TYPE', 'HOME', 'TEL', 0);
+    * $vcard->addParam('TYPE', 'PREF', 'TEL', 0);
+    *
+    * @param string $param_name  The parameter name, such as TYPE, VALUE,
+    * or ENCODING.
+    *
+    * @param string $param_value The parameter value.
+    *
+    * @param string $prop        The vCard property for which this is a
+    * paramter (ADR, TEL, etc).  If null, will be the property that was
+    * last set or added-to.
+    *
+    * @param mixed  $iter        An integer vCard property iteration that this
+    * is a param for.  E.g., if you have more than one ADR property, 0
+    * refers to the first ADR, 1 to the second ADR, and so on.  If null,
+    * the parameter will be added to the last property iteration
+    * available.
+    *
+    * @return void
+    * @throws File_IMC_Excpetion on failure.
+    */
+    public function addParam($param_name, $param_value, $prop=null, $iter=null)
+    {
+        // if property is not specified,
+        //   default to the last property that was set or added.
+        if ($prop === null) {
+            $prop = $this->lastProp;
+        }
+        // if property is not specified,
+        //   default to the last iteration that was set or added.
+        if ($iter === null) {
+            //$iter = count($this->value[$prop]) - 1;
+            $iter = $this->lastIter;
+        }
+
+        // massage the text arguments
+        $prop			= strtoupper(trim($prop));
+        $param_name		= strtoupper(trim($param_name));
+        $param_value	= trim($param_value);
+
+        if ( !is_integer($iter) || $iter < 0) {
+            throw new File_IMC_Exception(
+                $iter.' is not a valid iteration number for '
+                .$prop.'; must be a positive integer.',
+                FILE_IMC::ERROR_INVALID_ITERATION
+            );
+        }
+
+        try {
+            $result = $this->validateParam($param_name, $param_value, $prop, $iter);
+            if ( $result ) {
+                $this->param[$prop][$iter][$param_name][] = $param_value;
+            }
+        } catch (File_IMC_Exception $e) {
+            throw $e; // FIXME: check later
+        }
+
+    }
+
+    /**
+    * Gets back the group for a given property.
+    *
+    * @param string $prop The property to get parameters for (ADR, TEL, etc).
+    *
+    * @param int    $iter The vCard property iteration to get the param
+    * list for. E.g., if you have more than one ADR property, 0 refers
+    * to the first ADR, 1 to the second ADR, and so on.
+    *
+    * @return string
+    */
+    public function getGroup($prop=null, $iter=null)
+    {
+        $prop = $prop == null
+            ? $this->lastProp
+            : trim(strtoupper($prop));
+        $iter = $iter == null
+            ? $this->lastIter
+            : $iter;
+        $text = isset($this->group[$prop][$iter])
+            ? $this->group[$prop][$iter]
+            : '';
+        return $text;
+    }
+
+    /**
+    * Sets the group for a given property.
+    *
+    * @param string $groupName The group to assign to the property
+    *
+    * @param string $prop      The property (ADR, TEL, etc).
+    *  If null, will be the property that was last set or added-to.
+    *
+    * @param int    $iter      An integer vCard property iteration
+    *  that this is a param for.    If null, will be the iteration
+    *  that was last set or added-to.
+    *
+    * @return void
+    */
+    public function setGroup($groupName, $prop=null, $iter=null)
+    {
+        $prop = $prop === null
+            ? $this->lastProp
+            : trim(strtoupper($prop));
+        $iter = $iter === null
+            ? $this->lastIter
+            : $iter;
+        $this->group[$prop][$iter] = $groupName;
+        $this->_setLast($prop, $iter);
+    }
+
+    /**
+    * Gets the left-side/prefix/before-the-colon (metadata) part of a
+    * vCard line, including the property identifier, the parameter
+    * list, and a colon.
+    *
+    * @param string $prop The property to get metadata for (ADR, TEL, etc).
+    *
+    * @param int    $iter The vCard property iteration to get the metadata
+    * for. E.g., if you have more than one ADR property, 0 refers to
+    * the first ADR, 1 to the second ADR, and so on.
+    *
+    * @return string The line prefix metadata.
+    */
+    public function getMeta($prop, $iter = 0)
+    {
+        $text = '';
+        $group	= $this->getGroup($prop, $iter);
+        $params	= $this->getParam($prop, $iter);
+        if ( !empty($group) ) {
+            $text .= $group.'.';
+        }
+        $text .= $prop;
+        if ( trim($params) != '' ) {
+            $text .= ';'.$params;
+        }
+        $text .= ':';
+        return $text;
+    }
+
+    /**
+    * Generic, all-purpose method to store a string or array in
+    * $this->value, in a way suitable for later output as a vCard
+    * element.  This forces the value to be the passed text or array
+    * value, overriding any prior values.
+    *
+    * @param string $prop  The property to set the value for ('N','ADR', etc).
+    *
+    * @param int    $iter  The property-iteration to set the value for.
+    *
+    * @param int    $part  The part number of the property-iteration to set
+    * the value for.
+    *
+    * @param mixed  $value A string or array; the set of repeated values
+    * for this property-iteration part.
+    *
+    * @return void
+    */
+    public function setValue($prop, $iter, $part, $value)
+    {
+        $prop = strtoupper($prop);
+        settype($value, 'array');
+        if ( !isset($this->value[$prop]) ) {
+            $this->value[$prop] = array();
+        }
+        if ( !isset($this->value[$prop][$iter]) ) {
+            $this->value[$prop][$iter] = array();
+        }
+        $this->value[$prop][$iter][$part] = $value;
+        $this->_setLast($prop, $iter);
+    }
+
+    /**
+    * Generic, all-purpose method to add a repetition of a string or
+    * array in $this->value, in a way suitable for later output as a
+    * vCard element.  This appends the value to be the passed text or
+    * array value, leaving any prior values in place.
+    *
+    * @param string $prop The property to set the value for ('N',
+    * 'ADR', etc).
+    *
+    * @param int    $iter The property-iteration to set the value for.
+    *
+    * @param int    $part The part number of the property-iteration to set
+    * the value for.
+    *
+    * @param mixed  $vals A string or array; the set of repeated values
+    * for this property-iteration part.
+    *
+    * @return void
+    */
+    public function addValue($prop, $iter, $part, $vals)
+    {
+        $prop = strtoupper($prop);
+        settype($vals, 'array');
+        foreach ($vals as $val) {
+            $this->value[$prop][$iter][$part][] = $val;
+        }
+        $this->_setLast($prop, $iter);
+    }
+
+    /**
+    * Generic, all-purpose method to get back the data stored in $this->value.
+    *
+    * @param string $prop The property to set the value for ('N','ADR', etc).
+    *
+    * @param int    $iter The property-iteration to set the value for.
+    *
+    * @param int    $part The part number of the property-iteration to get
+    * the value for.
+    *
+    * @param mixed  $rept The repetition number within the part to get;
+    * if null, get all repetitions of the part within the iteration.
+    *
+    * @return string The value, escaped and delimited, of all
+    * repetitions in the property-iteration part (or specific
+    * repetition within the part).
+    */
+    public function getValue($prop, $iter=0, $part=0, $rept=null)
+    {
+        if ( $rept === null && is_array($this->value[$prop][$iter][$part]) ) {
+            // get all repetitions of a part
+            $list = array();
+            foreach ($this->value[$prop][$iter][$part] as $key => $val) {
+                $list[] = trim($val);
+            }
+            $this->escape($list);
+            return implode(',', $list);
+        } else {
+            // get a specific repetition of a part
+            $value = trim($this->value[$prop][$iter][$part][$rept]);
+            $this->escape($value);
+            return $value;
+        }
+    }
+
+    /**
+    * Gets back the parameter string for a given property.
+    *
+    * @param string $prop The property to get parameters for (ADR, TEL,
+    * etc).
+    *
+    * @param int    $iter The vCard property iteration to get the param
+    * list for. E.g., if you have more than one ADR property, 0 refers
+    * to the first ADR, 1 to the second ADR, and so on.
+    *
+    * @return string
+    */
+    public function getParam($prop, $iter = 0)
+    {
+        $prop = trim(strtoupper($prop));
+        $text = '';
+
+        if (!isset($this->param[$prop])) {
+            $this->param[$prop] = array();
+        }
+        if (!isset($this->param[$prop][$iter])
+            || !is_array($this->param[$prop][$iter])
+        ) {
+            // if there were no parameters, this will be blank.
+            return $text;
+        }
+
+        // loop through the array of parameters for
+        // the property
+
+        foreach ($this->param[$prop][$iter] as $param_name => $param_val) {
+
+            // if there were previous parameter names, separate with
+            // a semicolon
+            if ($text != '') {
+                $text .= ';';
+            }
+
+            if ($param_val === null) {
+
+                // no parameter value was specified, which is typical
+                // for vCard version 2.1 -- the name is the value.
+                $this->escape($param_name);
+                $text .= $param_name;
+
+            } else {
+                // set the parameter name...
+                $text .= strtoupper($param_name) . '=';
+
+                // ...then escape and comma-separate the parameter
+                // values.
+                $this->escape($param_val);
+                $text .= implode(',', $param_val);
+            }
+        }
+        // if there were no parameters, this will be blank.
+        return $text;
+    }
+
+    /**
+    * Builds a vCard/vCal from a parser result array.  Only send
+    * one vCard from the parse-results.
+    *
+    * Usage (to build from first vCard in parsed results):
+    *
+    * $parse = File_IMC::parse('vCard'); // new parser
+    * $info = $parse->fromFile('sample.vcf'); // parse file
+    *
+    * $vcard = File_IMC::build('vCard'); // new builder
+    * $vcard->setFromArray($info);
+    *
+    * @param array $src One vCard entry as parsed using File_IMC::parse()
+    *
+    * @return void
+    *
+    * @see File_IMC_Parse::fromFile()
+    * @see File_IMC_Parse::fromText()
+    */
+    public function setFromArray(array $src)
+    {
+        // reset to a blank values and params
+        $this->value = array();
+        $this->param = array();
+        $this->group = array();
+        foreach ($src as $card => $card_val) {
+            // loop through properties (N, ADR, TEL, etc)
+            foreach ($card_val AS $prop => $prop_val) {
+                $prop = strtoupper($prop);
+                $this->lastProp = $prop;
+                // iteration number of each property
+                foreach ($prop_val AS $iter => $iter_val) {
+                    $this->lastIter = $iter;
+                    foreach ($iter_val AS $kind => $kind_val) {
+                        $kind = strtolower($kind);
+                        if ( $kind == 'group' ) {
+                            $this->group[$prop][$iter] = $kind_val;
+                        } elseif ( is_array($kind_val) ) {
+                            foreach ( $kind_val AS $part => $part_val ) {
+                                foreach ( $part_val AS $rept => $text ) {
+                                    if ( $kind == 'value' ) {
+                                        $this->value[$prop][$iter][$part][$rept] = $text;
+                                    } elseif ( $kind == 'param' ) {
+                                        $this->param[$prop][$iter][$part][$rept] = $text;
+                                    } else {
+                                        // ignore data when $kind is neither 'value' nor 'param'
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Magic method to display the vCard/vCal.
+     *
+     * 
+     *
+     * $vcard = File_IMC::build('vCard');
+     *
+     * // set "TYPE=HOME,PREF" for the first TEL property
+     * $vcard->addParam('TYPE', 'HOME', 'TEL', 0);
+     * $vcard->addParam('TYPE', 'PREF', 'TEL', 0);
+     *
+     * echo $vcard;
+     *
+     * 
+     *
+     * @return string
+     * @uses   self::fetch()
+     */
+    public function __toString()
+    {
+        return $this->fetch();
+    }
 }
 
-if ( !function_exists('quoted_printable_encode') )
-{
-	/**
-	 * quoted_printable_encode()
-	 * PHP 5.3.0
-	 * http://us.php.net/manual/en/function.quoted-printable-encode.php
-	 *    ericth at NOSPAM dot pennyworth dot com 07-Oct-2011 01:46
-	 * @param string $string
-	 * @return string
-	 */
-	function quoted_printable_encode($str)
-	{
-		$lp = 0;
-		$ret = '';
-		$hex = "0123456789ABCDEF";
-		$PHP_QPRINT_MAXL = 75;
-		$length = strlen($str);
-		$str_index = 0;
-		while ($length--) {
-			if ( (($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0 ) {
-				$ret .= "\015";
-				$ret .= $str[$str_index++];
-				$length--;
-				$lp = 0;
-			} else {
-				if ( ctype_cntrl($c)
-					|| (ord($c) == 0x7f)
-					|| (ord($c) & 0x80)
-					|| ($c == '=')
-					|| (($c == ' ') && ($str[$str_index] == "\015")) )
-				{
-					if (($lp += 3) >$PHP_QPRINT_MAXL)
-					{
-						$ret .= '=';
-						$ret .= "\015";
-						$ret .= "\012";
-						$lp = 3;
-					}
-					$ret .= '=';
-					$ret .= $hex[ord($c) >> 4];
-					$ret .= $hex[ord($c) & 0xf];
-				}
-				else
-				{
-					if ( (++$lp) > $PHP_QPRINT_MAXL )
-					{
-						$ret .= '=';
-						$ret .= "\015";
-						$ret .= "\012";
-						$lp = 1;
-					}
-					$ret .= $c;
-				}
-			}
-		}
-		return $ret;
-	}
+if ( !function_exists('quoted_printable_encode') ) {
+    /**
+     * quoted_printable_encode()
+     * PHP 5.3.0
+     * http://us.php.net/manual/en/function.quoted-printable-encode.php
+     *    ericth at NOSPAM dot pennyworth dot com 07-Oct-2011 01:46
+     *
+     * @param string $str string to encode
+     *
+     * @return string
+     */
+    function quoted_printable_encode($str)
+    {
+        $lp = 0;
+        $ret = '';
+        $hex = "0123456789ABCDEF";
+        $PHP_QPRINT_MAXL = 75;
+        $length = strlen($str);
+        $str_index = 0;
+        while ($length--) {
+            if ( (($c = $str[$str_index++]) == "\015") && ($str[$str_index] == "\012") && $length > 0 ) {
+                $ret .= "\015";
+                $ret .= $str[$str_index++];
+                $length--;
+                $lp = 0;
+            } else {
+                if ( ctype_cntrl($c)
+                    || (ord($c) == 0x7f)
+                    || (ord($c) & 0x80)
+                    || ($c == '=')
+                    || (($c == ' ') && ($str[$str_index] == "\015"))
+                ) {
+                    if (($lp += 3) >$PHP_QPRINT_MAXL) {
+                        $ret .= '=';
+                        $ret .= "\015";
+                        $ret .= "\012";
+                        $lp = 3;
+                    }
+                    $ret .= '=';
+                    $ret .= $hex[ord($c) >> 4];
+                    $ret .= $hex[ord($c) & 0xf];
+                } else {
+                    if ( (++$lp) > $PHP_QPRINT_MAXL ) {
+                        $ret .= '=';
+                        $ret .= "\015";
+                        $ret .= "\012";
+                        $lp = 1;
+                    }
+                    $ret .= $c;
+                }
+            }
+        }
+        return $ret;
+    }
 }
-
-?>
\ No newline at end of file

From c73d558772151dd65433a892c5ab98ba186c2435 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Thu, 4 Apr 2013 16:01:27 -0500
Subject: [PATCH 13/17] Update Parse.php

after running phpCS
---
 File/IMC/Parse.php | 1282 ++++++++++++++++++++++----------------------
 1 file changed, 633 insertions(+), 649 deletions(-)

diff --git a/File/IMC/Parse.php b/File/IMC/Parse.php
index 8f23e31..307beb0 100644
--- a/File/IMC/Parse.php
+++ b/File/IMC/Parse.php
@@ -1,6 +1,6 @@
 
@@ -50,11 +52,10 @@
 * specific elements that need special decoding.  For an example, see
 * File_IMC_Parse_vCard.
 *
-* @author Paul M. Jones 
-*
 * @category File_Formats
 * @package  File_IMC
 * @author   Paul M. Jones 
+* @author   Paul M. Jones 
 * @author   Till Klampaeckel 
 * @license  http://www.opensource.org/licenses/bsd-license.php The BSD License
 * @version  Release: @package_version@
@@ -63,651 +64,634 @@
 abstract class File_IMC_Parse
 {
     /**
-	* Keeps track of the current line being parsed
-	*
-	* Starts at -1 so that the first line parsed is 0, since
-	* _parseBlock() advances the counter by 1 at the beginning
-	*
-	* @see self::_parseBlock()
-	*
-	* @var int
-	*/
-	protected $count = -1;
-
-	/**
-	 * @var array
-	 */
-	protected $data;
-
-	abstract function getVersion();
-
-	/**
-	* Reads a file for parsing, then sends it to $this->fromText()
-	* and returns the results.
-	*
-	* @param string $filename The name of the file to read
-	* @param string $charset what charset the text should be.  Default = UTF-8
-	*
-	* @return array An array of information extracted from the file.
-	* @throws File_IMC_Exception If the file does not exist.
-	* @throws File_IMC_Exception If the file is not readable.
-	*
-	* @see self::fromText()
-	* @see self::_fromArray()
-	*/
-	public function fromFile($filename, $charset='UTF-8')
-	{
-		if (!file_exists($filename)) {
-			throw new File_IMC_Exception("File {$filename} does not exist.");
-		}
-		if (!is_readable($filename)) {
-			throw new File_IMC_Exception("Could not open {$filename}.");
-		}
-		// get the file data
-		$text = implode('', file($filename));
-
-		// dump to, and get return from, the fromText() method.
-		return $this->fromText($text,$charset);
-	}
-
-	/**
-	* Prepares a block of text for parsing, then sends it through and
-	* returns the results from $this->_fromArray().
-	*
-	* @param string $text A block of text to read for information.
-	* @param string $charset what charset the text should be.  Default = UTF-8
-	* @return array An array of information extracted from the source text.
-	*
-	* @see self::_fromArray()
-	*/
-	public function fromText($text, $charset='UTF-8')
-	{
-		// convert all kinds of line endings to Unix-standard and get
-		// rid of double blank lines.
-		$text = $this->_convertLineEndings($text);
-
-		// unfold lines.  concat two lines where line 1 ends in \n and
-		// line 2 starts with any amount of whitespace.  only removes
-		// the first whitespace character, leaves others in place.
-		//$fold_regex = '(\n)([ |\t])';
-		$fold_regex = '(\n)([ |\t|=])';
-		$text       = preg_replace("/$fold_regex/i", "", $text);
-
-		// convert the resulting text to an array of lines
-		$lines = explode("\n", $text);
-
-		// parse the array of lines and return info
-		$this->data = $this->_fromArray($lines,$charset);
-		return $this->data;
-	}
-
-	/**
-	* Converts line endings in text.
-	*
-	* Takes any text block and converts all line endings to UNIX
-	* standard. DOS line endings are \r\n, Mac are \r, and UNIX is \n.
-	* As a side-effect, all double-newlines (\n\n) are converted to a
-	* single-newline.
-	*
-	* NOTE: Acts on the text block in-place; does not return a value.
-	*
-	* @param string $text The string on which to convert line endings.
-	*
-	* @return void
-	*/
-	protected function _convertLineEndings($text)
-	{
-		// first, replace \r\n with \n to fix up from DOS and Mac
-		$text = str_replace("\r\n", "\n", $text);
-		$text = str_replace("\r", "\n", $text);
-
-		return $text;
-	}
-
-	/**
-	* Splits a string into an array.  Honors backslash-escaped
-	* delimiters, (i.e., splits at ';' not '\;') and double-quotes
-	* (will not break inside double-quotes ("")).
-	*
-	* @param string $text The string to split into an array.
-	*
-	* @param string $delim Character to split string at.
-	*
-	* @param bool $recurse If true, recursively parse the entire text
-	* for all occurrences of the delimiter; if false, only parse for
-	* the first occurrence.  Defaults to true.
-	*
-	* @return string|array An array of values, or a single string.
-	*/
-	public function _splitByDelim($text, $delim, $recurse = true)
-	{
-		// where in the string is the delimiter?
-		$pos = false;
-
-		// was the previously-read character a backslash?
-		// (used for tracking escaped characters)
-		$prevIsBackslash = false;
-
-		// are we currently inside a quoted passage?
-		$inQuotes = false;
-
-		// the length of the text to be parsed
-		$len = strlen($text);
-
-		// go through the text character by character, find the
-		// first occurrence of the delimiter, save it, and
-		// recursively parse the rest of the text
-		for ($i = 0; $i < $len; $i++) {
-
-			// if the current char is a double-quote, and the
-			// previous char was _not_ an escaping backslash,
-			// then note that we are now inside a quoted passage.
-			if ($text{$i} == '"' && $prevIsBackslash == false) {
-				($inQuotes == true) ? $inQuotes = false : $inQuotes = true;
-			}
-
-			// if the current char is the delimiter, and we are _not_
-			// inside quotes, and the delimiter has not been backslash-
-			// escaped, then note the position of the delimiter and
-			// break out of the loop.
-			if ($text{$i} == $delim &&
-				$inQuotes == false &&
-				$prevIsBackslash == false) {
-
-				$pos = $i;
-				break;
-			}
-
-			// we have not found quotes, or the delimiter.
-			// is the current char an escaping backslash?
-			if ($text{$i} == "\\") {
-				$prevIsBackslash = true;
-			} else {
-				$prevIsBackslash = false;
-			}
-		}
-
-		// have we found the delimiter in the text?
-		if ($pos === false) {
-			// we have not found the delimiter anywhere in the
-			// text.  return the text as it is.
-			return array($text);
-		}
-
-		// find the portions of the text to the left and the
-		// right of the delimiter
-		$left = trim(substr($text, 0, $pos));
-		$right = trim(substr($text, $pos+1, strlen($text)));
-
-		// should we recursively parse the rest of the text?
-		if ($recurse) {
-			// parse the right portion for the same delimiter, and
-			// merge the results with the left-portion.
-			return array_merge(
-				array($left),
-				$this->_splitByDelim($right, $delim, $recurse)
-			);
-		}
-
-		// no recursion
-		return array($left, $right);
-	}
-
-	/**
-	* Splits a string into an array at semicolons.
-	*
-	* @param string $text The string to split into an array.
-	*
-	* @param bool $convertSingle If splitting the string results in a
-	* single array element, return a string instead of a one-element
-	* array.
-	*
-	* @param bool $recurse If true, recursively parse the entire text
-	* for all occurrences of the delimiter; if false, only parse for
-	* the first occurrence.  Defaults to true.
-	*
-	* @return string|array An array of values, or a single string.
-	*
-	* @see self::_splitByDelim()
-	*/
-	protected function _splitBySemi($text, $recurse = true)
-	{
-		return $this->_splitByDelim($text, ";", $recurse);
-	}
-
-	/**
-	* Splits a string into an array at commas.
-	*
-	* @param string $text The string to split into an array.
-	*
-	* @param bool $recurse If true, recursively parse the entire text
-	* for all occurrences of the delimiter; if false, only parse for
-	* the first occurrence.  Defaults to true.
-	*
-	* @return string|array An array of values, or a single string.
-	*
-	* @see self::_splitByDelim()
-	*/
-	protected function _splitByComma($text, $recurse = true)
-	{
-		return $this->_splitByDelim($text, ",", $recurse);
-	}
-
-	/**
-	*
-	* Splits the line into types/parameters and values.
-	*
-	* @todo A parameter w/ 1 quote will break everything. Try to
-	*       come up with a good way to fix this.
-	*
-	* @param string $text The string to split into an array.
-	*
-	* @param bool $recurse If true, recursively parse the entire text
-	* for all occurrences of the delimiter; if false, only parse for
-	* the first occurrence.  Defaults to false (this is different from
-	* {@link self::_splitByCommon()} and {@link self::_splitBySemi()}).
-	*
-	* @return array The first element contains types and parameters
-	* (before the colon). The second element contains the line's value
-	* (after the colon).
-	*/
-	protected function _splitByColon($text, $recurse = false)
-	{
-		return $this->_splitByDelim($text, ":", $recurse);
-	}
-
-	/**
-	* Used to make string human-readable after being a vCard value.
-	*
-	* Converts...
-	*     \; => ;
-	*     \, => ,
-	*     literal \n => newline
-	*
-	* @param string|array $text The text to unescape.
-	*
-	* @return mixed
-	*/
-	protected function _unescape($text)
-	{
-		if (is_array($text)) {
-			foreach ($text as $key => $val) {
-				$text[$key] = $this->_unescape($val);
-			}
-		} else {
-			// \r added per #16637
-			$find    = array('\:', '\;', '\,', '\n', '\r');
-			$replace = array(':',  ';',  ',',  "\n", "\r");
-			$text    = str_replace($find, $replace, $text);
-		}
-		return $text;
-	}
-
-	/**
-	* Parses an array of source lines and returns an array of vCards.
-	* Each element of the array is itself an array expressing the types,
-	* parameters, and values of each part of the vCard. Processes both
-	* 2.1 and 3.0 vCard sources.
-	*
-	* @param array $source An array of lines to be read for vCard information.
-	* @param string $charset what charset the text should be.  Default = UTF-8
-	* @return array An array of of vCard information extracted from the source array.
-	*
-	* @todo fix missing colon = skip line
-	*/
-	protected function _fromArray($source,$charset='UTF-8')
-	{
-		$parsed = $this->_parseBlock($source,$charset);
-		$parsed = $this->_unescape($parsed);
-		return $parsed;
-	}
-
-	/**
-	* Goes through the IMC file, recursively processing BEGIN-END blocks
-	*
-	* Handles nested blocks, such as vEvents (BEGIN:VEVENT) and vTodos
-	* (BEGIN:VTODO) inside vCalendars (BEGIN:VCALENDAR).
-	*
-	* @param array Array of lines in the IMC file
-	* @param string $charset
-	*
-	* @return array
-	*/
-	protected function _parseBlock(array $source, $charset='UTF-8')
-	{
-		$max = count($source);
-
-		for ($this->count++; $this->count < $max; $this->count++) {
-
-			$line = $source[$this->count];
-
-			// if the line is blank, skip it.
-			if (trim($line) == '') {
-				continue;
-			}
-
-			// get the left and right portions. The part
-			// to the left of the colon is the type and parameters;
-			// the part to the right of the colon is the value data.
-			$lr = $this->_splitByColon($line);
-			if ( count($lr) < 2 ) {
-				// colon not found, skip whole line
-				continue;
-			}
-			list($left,$right) = $lr;
-
-			if (strtoupper($left) == "BEGIN") {
-
-				$block[$right][] = $this->_parseBlock($source);
-
-			} elseif (strtoupper($left) == "END") {
-
-				return $block;
-
-			} else {
-
-				// we're not on an ending line, so collect info from
-				// this line into the current card. split the
-				// left-portion of the line into a type-definition
-				// (the kind of information) and parameters for the
-				// type.
-				$tmp     = $this->_splitBySemi($left);
-				$group   = $this->_getGroup($tmp);
-				$typedef = $this->_getTypeDef($tmp);
-				$params  = $this->_getParams($tmp);
-
-				$resp = $this->_decode($params, $right, $typedef);
-				$params = $resp[0];
-				$right  = $resp[1];
-
-				$resp = $this->_charset_conv($params, $right, $charset);
-				$params = $resp[0];
-				$right  = $resp[1];
-
-				// now get the value-data from the line, based on the typedef
-				$func = '_parse' . strtoupper($typedef);
-
-				if (method_exists($this, $func)) {
-					$value = $this->$func($right);
-				} else {
-					// by default, just grab the plain value. keep
-					// as an array to make sure *all* values are
-					// arrays.  for consistency. ;-)
-					$value = array(array($right));
-				}
-
-				// add the type, parameters, and value to the
-				// current card array.  note that we allow multiple
-				// instances of the same type, which might be dumb
-				// in some cases (e.g., N).
-				$block[$typedef][] = array(
-					'group' => $group,
-					'param' => $params,
-					'value' => $value
-				);
-			}
-		}
-		return $block;
-	}
-
-	/**
-	* Takes a line and extracts the Group for the line (a group is
-	* identified as a prefix-with-dot to the Type-Definition; e.g.,
-	* Group.ADR or Group.ORG).
-	*
-	* @param array $text Array containing left side (before colon) split by
-	*                    semi-colon from a line.
-	*
-	* @return string The group for the line.
-	*
-	* @see self::_getTypeDef()
-	* @see self::_splitBySemi()
-	*/
-	protected function _getGroup(array $text)
-	{
-		// find the first element (the typedef)
-		$tmp = $text[0];
-		// find a dot in the typedef
-		$pos = strpos($tmp, '.');
-		return $pos !== false
-			? substr($tmp, 0, $pos)	// there is a group, return it
-			: '';					// there is no group
-	}
-
-	/**
-	* Takes a line and extracts the Type-Definition for the line (not
-	* including the Group portion; e.g., in Group.ADR, only ADR is
-	* returned).
-	*
-	* @param array $text Array containing left side (before colon) split by
-	*                    semi-colon from a line.
-	*
-	* @return string The type definition for the line.
-	*
-	* @see self::_getGroup()
-	* @see self::_splitBySemi()
-	*/
-	protected function _getTypeDef(array $text)
-	{
-		// find the first element (the typedef)
-		$tmp = strtoupper($text[0]);
-		$pos = strpos($tmp,'.');
-		return $pos !== false
-			? substr($tmp, $pos + 1)	// there is a group... just return the property name
-			: $tmp;						// no group
-
-	}
-
-	/**
-	* Finds the Type-Definition parameters for a line.
-	*
-	* @param array Array containing left side (before colon) split by
-	*              semi-colon from a line.
-	*
-	* @return array An array of parameters.
-	*
-	* @see self::_splitBySemi()
-	*/
-	protected function _getParams(array $text)
-	{
-		// drop the first element of the array (the type-definition)
-		array_shift($text);
-
-		// set up an array to retain the parameters, if any
-		$params = array();
-
-		// loop through each parameter.  the params may be in the format...
-		// "TYPE=type1,type2,type3"
-		//    ...or...
-		// "TYPE=type1;TYPE=type2;TYPE=type3"
-		foreach ($text as $full) {
-
-			// split the full parameter at the equal sign so we can tell
-			// the parameter name from the parameter value
-			$tmp = explode("=", $full, 2);
-
-			// the key is the left portion of the parameter (before
-			// '='). if in 2.1 format, the key may in fact be the
-			// parameter value, not the parameter name.
-			$key = strtoupper(trim($tmp[0]));
-
-			// get the parameter name by checking to see if it's in
-			// vCard 2.1 or 3.0 format.
-			$name = $this->_getParamName($key);
-
-			// list of all parameter values
-			$listall = array_key_exists(1, $tmp) ? trim($tmp[1]) : '';
-
-			// if there is a value-list for this parameter, they are
-			// separated by commas, so split them out too.
-			$list = $this->_splitByComma($listall);
-
-			// now loop through each value in the parameter and retain
-			// it.  if the value is blank, that means it's a 2.1-style
-			// param, and the key itself is the value.
-			foreach ($list as $val) {
-				if (trim($val) != '') {
-					// 3.0 formatted parameter
-					$params[$name][] = trim($val);
-				} else {
-					// 2.1 formatted parameter
-					$params[$name][] = $key;
-				}
-			}
-
-			// if, after all this, there are no parameter values for the
-			// parameter name, retain no info about the parameter (saves
-			// ram and checking-time later).
-			if (count($params[$name]) == 0) {
-				unset($params[$name]);
-			}
-		}
-
-		// return the parameters array.
-		return $params;
-	}
-
-	/**
-	* Returns the parameter name for parameters given without names.
-	*
-	* The vCard 2.1 specification allows parameter values without a
-	* name. The parameter name is then determined from the unique
-	* parameter value.
-	*
-	* Shamelessly lifted from Frank Hellwig  and his
-	* vCard PHP project .
-	*
-	* @param string $value The first element in a parameter name-value
-	* pair.
-	*
-	* @return string The proper parameter name (TYPE, ENCODING, or
-	* VALUE).
-	*/
-	protected function _getParamName($value)
-	{
-		static $types = array (
-			'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
-			'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER',
-			'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
-			'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD',
-			'INTERNET', 'IBMMAIL', 'MCIMAIL',
-			'POWERSHARE', 'PRODIGY', 'TLX', 'X400',
-			'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB',
-			'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME',
-			'MPEG', 'MPEG2', 'AVI',
-			'WAVE', 'AIFF', 'PCM',
-			'X509', 'PGP'
-		);
-
-		// CONTENT-ID added by pmj
-		static $values = array (
-			'INLINE', 'URL', 'CID', 'CONTENT-ID'
-		);
-
-		// 8BIT added by pmj
-		static $encodings = array (
-			'7BIT', '8BIT', 'QUOTED-PRINTABLE', 'BASE64'
-		);
-
-		// changed by pmj to the following so that the name defaults to
-		// whatever the original value was.  Frank Hellwig's original
-		// code was "$name = 'UNKNOWN'".
-		$name = $value;
-
-		if (in_array($value, $types)) {
-			$name = 'TYPE';
-		} elseif (in_array($value, $values)) {
-			$name = 'VALUE';
-		} elseif (in_array($value, $encodings)) {
-			$name = 'ENCODING';
-		}
-
-		return $name;
-	}
-
-	/**
-	* Looks at a line's parameters;
-	* if ENCODING parameter is set and is QUOTED-PRINTABLE
-	*     then decode the text in-place.
-	* This method is overwritten in Parse/Vcard.php
-	*
-	* @access private
-	*
-	* @param array $params A parameter array from a vCard line.
-	* @param string $text A right-part (after-the-colon part) from a line.
-	* @param string $prop The property name.. passed so may have special-cases
-	* @return array
-	*
-	* @uses quoted_printable_decode()
-	*/
-	protected function _decode(array $params, $text,$prop=null)
-	{
-		// loop through each parameter
-		foreach ( $params as $param => $param_vals ) {
-			// check to see if it's an encoding param
-			if ( trim(strtoupper($param)) != 'ENCODING' ) {
-				continue;
-			}
-			// loop through each ENCODING param value
-			foreach ( $param_vals as $k => $param_val ) {
-				// if any of the values are QP, decode the text in-place and return
-				if ( trim(strtoupper($param_val)) == 'QUOTED-PRINTABLE' ) {
-					$text = quoted_printable_decode($text);
-					// remove the encoding param.. as it's no longer encoded!
-					unset($params[$param][$k]);
-					break;
-				}
-			}
-		}
-		return array($params, $text);
-	}
-
-	/**
-	* Convert text to the specified charaster set
-	*
-	* @access private
-	*
-	* @param array $params A parameter array from a vCard line.
-	* @param string $text A right-part (after-the-colon part) from a line.
-	* @param string $charset The charset to convert to
-	* @return array
-	*/
-	protected function _charset_conv(array $params, $text, $charset='UTF-8')
-	{
-		/*
-		vcard 2.1 default charset = ASCII.
-		vcard 3.0 default charset = UTF-8 // there is no CHARSET parameter
-		*/
-		$charset_is = 'UTF-8';
-		foreach ( $params as $param => $param_vala ) {
-			if ( trim(strtoupper($param)) == 'CHARSET' ) {
-				$charset_is = $param_vala[0];
-				break;
-			}
-		}
-		if ( $charset_is == 'ASCII' && $charset == 'UTF-8' ) {
-			$charset_is = 'UTF-8';
-			// no conversion necessary
-			// unset($params['CHARSET']);
-		}
-		elseif ( $charset_is != $charset ) {
-			if ( extension_loaded('mbstring') ) {
-				$text = mb_convert_encoding($text, $charset, $charset_is);
-				$params['CHARSET'] = array( $charset );
-			}
-			else {
-				$text_new = htmlentities($text,ENT_NOQUOTES,$charset_is);
-				$text_new = html_entity_decode($text_new,ENT_QUOTES,$charset);
-				if ( $text_new )
-				{
-					$text = $text_new;
-					$params['CHARSET'] = array( $charset );
-				}
-			}
-		}
-		return array($params, $text);
-	}
+    * Keeps track of the current line being parsed
+    *
+    * Starts at -1 so that the first line parsed is 0, since
+    * _parseBlock() advances the counter by 1 at the beginning
+    *
+    * @see self::_parseBlock()
+    *
+    * @var int
+    */
+    protected $count = -1;
+
+    /**
+     * @var array
+     */
+    protected $data;
+
+    /**
+     * Return version.
+     *
+     * @uses parent::$data
+     * @return string
+     */
+    abstract function getVersion();
+
+    /**
+    * Reads a file for parsing, then sends it to $this->fromText()
+    * and returns the results.
+    *
+    * @param string $filename The name of the file to read
+    * @param string $charset  what charset the text should be.  Default = UTF-8
+    *
+    * @return array An array of information extracted from the file.
+    * @throws File_IMC_Exception If the file does not exist.
+    * @throws File_IMC_Exception If the file is not readable.
+    *
+    * @see self::fromText()
+    * @see self::_fromArray()
+    */
+    public function fromFile($filename, $charset='UTF-8')
+    {
+        if (!file_exists($filename)) {
+            throw new File_IMC_Exception("File {$filename} does not exist.");
+        }
+        if (!is_readable($filename)) {
+            throw new File_IMC_Exception("Could not open {$filename}.");
+        }
+        // get the file data
+        $text = implode('', file($filename));
+        // dump to, and get return from, the fromText() method.
+        return $this->fromText($text, $charset);
+    }
+
+    /**
+    * Prepares a block of text for parsing, then sends it through and
+    * returns the results from $this->_fromArray().
+    *
+    * @param string $text    A block of text to read for information.
+    * @param string $charset what charset the text should be.  Default = UTF-8
+    *
+    * @return array An array of information extracted from the source text.
+    *
+    * @see self::_fromArray()
+    */
+    public function fromText($text, $charset='UTF-8')
+    {
+        // convert all kinds of line endings to Unix-standard and get
+        // rid of double blank lines.
+        $text = $this->_convertLineEndings($text);
+
+        // unfold lines.  concat two lines where line 1 ends in \n and
+        // line 2 starts with any amount of whitespace.  only removes
+        // the first whitespace character, leaves others in place.
+        //$fold_regex = '(\n)([ |\t])';
+        $fold_regex = '(\n)([ |\t|=])';
+        $text       = preg_replace("/$fold_regex/i", "", $text);
+
+        // convert the resulting text to an array of lines
+        $lines = explode("\n", $text);
+
+        // parse the array of lines and return info
+        $this->data = $this->_fromArray($lines, $charset);
+        return $this->data;
+    }
+
+    /**
+    * Converts line endings in text.
+    *
+    * Takes any text block and converts all line endings to UNIX
+    * standard. DOS line endings are \r\n, Mac are \r, and UNIX is \n.
+    * As a side-effect, all double-newlines (\n\n) are converted to a
+    * single-newline.
+    *
+    * NOTE: Acts on the text block in-place; does not return a value.
+    *
+    * @param string $text The string on which to convert line endings.
+    *
+    * @return void
+    */
+    protected function _convertLineEndings($text)
+    {
+        // first, replace \r\n with \n to fix up from DOS and Mac
+        $text = str_replace("\r\n", "\n", $text);
+        $text = str_replace("\r", "\n", $text);
+
+        return $text;
+    }
+
+    /**
+    * Splits a string into an array.  Honors backslash-escaped
+    * delimiters, (i.e., splits at ';' not '\;') and double-quotes
+    * (will not break inside double-quotes ("")).
+    *
+    * @param string $text    The string to split into an array.
+    *
+    * @param string $delim   Character to split string at.
+    *
+    * @param bool   $recurse If true, recursively parse the entire text
+    * for all occurrences of the delimiter; if false, only parse for
+    * the first occurrence.  Defaults to true.
+    *
+    * @return string|array An array of values, or a single string.
+    */
+    public function _splitByDelim($text, $delim, $recurse = true)
+    {
+        // where in the string is the delimiter?
+        $pos = false;
+
+        // was the previously-read character a backslash?
+        // (used for tracking escaped characters)
+        $prevIsBackslash = false;
+
+        // are we currently inside a quoted passage?
+        $inQuotes = false;
+
+        // the length of the text to be parsed
+        $len = strlen($text);
+
+        // go through the text character by character, find the
+        // first occurrence of the delimiter, save it, and
+        // recursively parse the rest of the text
+        for ($i = 0; $i < $len; $i++) {
+            // if the current char is a double-quote, and the
+            // previous char was _not_ an escaping backslash,
+            // then note that we are now inside a quoted passage.
+            if ($text{$i} == '"' && $prevIsBackslash == false) {
+                ($inQuotes == true) ? $inQuotes = false : $inQuotes = true;
+            }
+            // if the current char is the delimiter, and we are _not_
+            // inside quotes, and the delimiter has not been backslash-
+            // escaped, then note the position of the delimiter and
+            // break out of the loop.
+            if ( $text{$i} == $delim
+                && $inQuotes == false
+                && $prevIsBackslash == false
+            ) {
+                $pos = $i;
+                break;
+            }
+            // we have not found quotes, or the delimiter.
+            // is the current char an escaping backslash?
+            if ($text{$i} == "\\") {
+                $prevIsBackslash = true;
+            } else {
+                $prevIsBackslash = false;
+            }
+        }
+
+        // have we found the delimiter in the text?
+        if ($pos === false) {
+            // we have not found the delimiter anywhere in the
+            // text.  return the text as it is.
+            return array($text);
+        }
+
+        // find the portions of the text to the left and the
+        // right of the delimiter
+        $left = trim(substr($text, 0, $pos));
+        $right = trim(substr($text, $pos+1, strlen($text)));
+
+        // should we recursively parse the rest of the text?
+        if ($recurse) {
+            // parse the right portion for the same delimiter, and
+            // merge the results with the left-portion.
+            return array_merge(
+                array($left),
+                $this->_splitByDelim($right, $delim, $recurse)
+            );
+        }
+
+        // no recursion
+        return array($left, $right);
+    }
+
+    /**
+    * Splits a string into an array at semicolons.
+    *
+    * @param string $text    The string to split into an array.
+    * @param bool   $recurse If true, recursively parse the entire text
+    *   for all occurrences of the delimiter; if false, only parse for
+    *   the first occurrence.  Defaults to true.
+    *
+    * @return string|array An array of values, or a single string.
+    *
+    * @see self::_splitByDelim()
+    */
+    protected function _splitBySemi($text, $recurse = true)
+    {
+        return $this->_splitByDelim($text, ";", $recurse);
+    }
+
+    /**
+    * Splits a string into an array at commas.
+    *
+    * @param string $text    The string to split into an array.
+    * @param bool   $recurse If true, recursively parse the entire text
+    * for all occurrences of the delimiter; if false, only parse for
+    * the first occurrence.  Defaults to true.
+    *
+    * @return string|array An array of values, or a single string.
+    *
+    * @see self::_splitByDelim()
+    */
+    protected function _splitByComma($text, $recurse = true)
+    {
+        return $this->_splitByDelim($text, ",", $recurse);
+    }
+
+    /**
+    * Splits the line into types/parameters and values.
+    *
+    * @param string $text    The string to split into an array.
+    *
+    * @param bool   $recurse If true, recursively parse the entire text
+    * for all occurrences of the delimiter; if false, only parse for
+    * the first occurrence.  Defaults to false (this is different from
+    * {@link self::_splitByCommon()} and {@link self::_splitBySemi()}).
+    *
+    * @return array The first element contains types and parameters
+    * (before the colon). The second element contains the line's value
+    * (after the colon).
+    *
+    * @todo A parameter w/ 1 quote will break everything. Try to
+    *       come up with a good way to fix this.
+    */
+    protected function _splitByColon($text, $recurse = false)
+    {
+        return $this->_splitByDelim($text, ":", $recurse);
+    }
+
+    /**
+    * Used to make string human-readable after being a vCard value.
+    *
+    * Converts...
+    *     \; => ;
+    *     \, => ,
+    *     literal \n => newline
+    *
+    * @param string|array $text The text to unescape.
+    *
+    * @return mixed
+    */
+    protected function _unescape($text)
+    {
+        if (is_array($text)) {
+            foreach ($text as $key => $val) {
+                $text[$key] = $this->_unescape($val);
+            }
+        } else {
+            // \r added per #16637
+            $find    = array('\:', '\;', '\,', '\n', '\r');
+            $replace = array(':',  ';',  ',',  "\n", "\r");
+            $text    = str_replace($find, $replace, $text);
+        }
+        return $text;
+    }
+
+    /**
+    * Parses an array of source lines and returns an array of vCards.
+    * Each element of the array is itself an array expressing the types,
+    * parameters, and values of each part of the vCard. Processes both
+    * 2.1 and 3.0 vCard sources.
+    *
+    * @param array  $source  An array of lines to be read for vCard information.
+    * @param string $charset what charset the text should be.  Default = UTF-8
+    *
+    * @return array An array of of vCard information extracted from the source array.
+    *
+    * @todo fix missing colon = skip line
+    */
+    protected function _fromArray($source,$charset='UTF-8')
+    {
+        $parsed = $this->_parseBlock($source, $charset);
+        $parsed = $this->_unescape($parsed);
+        return $parsed;
+    }
+
+    /**
+    * Goes through the IMC file, recursively processing BEGIN-END blocks
+    *
+    * Handles nested blocks, such as vEvents (BEGIN:VEVENT) and vTodos
+    * (BEGIN:VTODO) inside vCalendars (BEGIN:VCALENDAR).
+    *
+    * @param array  $source  Array of lines in the IMC file
+    * @param string $charset default = UTF-8
+    *
+    * @return array
+    */
+    protected function _parseBlock(array $source, $charset='UTF-8')
+    {
+        $max = count($source);
+
+        for ($this->count++; $this->count < $max; $this->count++) {
+            $line = $source[$this->count];
+            // if the line is blank, skip it.
+            if (trim($line) == '') {
+                continue;
+            }
+            // get the left and right portions. The part
+            // to the left of the colon is the type and parameters;
+            // the part to the right of the colon is the value data.
+            $lr = $this->_splitByColon($line);
+            if ( count($lr) < 2 ) {
+                // colon not found, skip whole line
+                continue;
+            }
+            list($left,$right) = $lr;
+            if (strtoupper($left) == "BEGIN") {
+                $block[$right][] = $this->_parseBlock($source);
+            } elseif (strtoupper($left) == "END") {
+                return $block;
+            } else {
+                // we're not on an ending line, so collect info from
+                // this line into the current card. split the
+                // left-portion of the line into a type-definition
+                // (the kind of information) and parameters for the
+                // type.
+                $tmp     = $this->_splitBySemi($left);
+                $group   = $this->_getGroup($tmp);
+                $typedef = $this->_getTypeDef($tmp);
+                $params  = $this->_getParams($tmp);
+
+                $resp = $this->_decode($params, $right, $typedef);
+                $params = $resp[0];
+                $right  = $resp[1];
+
+                $resp = $this->_charset_conv($params, $right, $charset);
+                $params = $resp[0];
+                $right  = $resp[1];
+
+                // now get the value-data from the line, based on the typedef
+                $func = '_parse' . strtoupper($typedef);
+
+                if (method_exists($this, $func)) {
+                    $value = $this->$func($right);
+                } else {
+                    // by default, just grab the plain value. keep
+                    // as an array to make sure *all* values are
+                    // arrays.  for consistency. ;-)
+                    $value = array(array($right));
+                }
+
+                // add the type, parameters, and value to the
+                // current card array.  note that we allow multiple
+                // instances of the same type, which might be dumb
+                // in some cases (e.g., N).
+                $block[$typedef][] = array(
+                    'group' => $group,
+                    'param' => $params,
+                    'value' => $value
+                );
+            }
+        }
+        return $block;
+    }
+
+    /**
+    * Takes a line and extracts the Group for the line (a group is
+    * identified as a prefix-with-dot to the Type-Definition; e.g.,
+    * Group.ADR or Group.ORG).
+    *
+    * @param array $text Array containing left side (before colon) split by
+    *                    semi-colon from a line.
+    *
+    * @return string The group for the line.
+    *
+    * @see self::_getTypeDef()
+    * @see self::_splitBySemi()
+    */
+    protected function _getGroup(array $text)
+    {
+        // find the first element (the typedef)
+        $tmp = $text[0];
+        // find a dot in the typedef
+        $pos = strpos($tmp, '.');
+        return $pos !== false
+            ? substr($tmp, 0, $pos)	// there is a group, return it
+            : '';					// there is no group
+    }
+
+    /**
+    * Takes a line and extracts the Type-Definition for the line (not
+    * including the Group portion; e.g., in Group.ADR, only ADR is
+    * returned).
+    *
+    * @param array $text Array containing left side (before colon) split by
+    *                    semi-colon from a line.
+    *
+    * @return string The type definition for the line.
+    *
+    * @see self::_getGroup()
+    * @see self::_splitBySemi()
+    */
+    protected function _getTypeDef(array $text)
+    {
+        // find the first element (the typedef)
+        $tmp = strtoupper($text[0]);
+        $pos = strpos($tmp, '.');
+        return $pos !== false
+            ? substr($tmp, $pos + 1)	// there is a group. just return the prop name
+            : $tmp;						// no group
+
+    }
+
+    /**
+    * Finds the Type-Definition parameters for a line.
+    *
+    * @param array $text Array containing left side (before colon) split by
+    *              semi-colon from a line.
+    *
+    * @return array An array of parameters.
+    *
+    * @see self::_splitBySemi()
+    */
+    protected function _getParams(array $text)
+    {
+        // drop the first element of the array (the type-definition)
+        array_shift($text);
+
+        // set up an array to retain the parameters, if any
+        $params = array();
+
+        // loop through each parameter.  the params may be in the format...
+        // "TYPE=type1,type2,type3"
+        //    ...or...
+        // "TYPE=type1;TYPE=type2;TYPE=type3"
+        foreach ($text as $full) {
+
+            // split the full parameter at the equal sign so we can tell
+            // the parameter name from the parameter value
+            $tmp = explode("=", $full, 2);
+
+            // the key is the left portion of the parameter (before
+            // '='). if in 2.1 format, the key may in fact be the
+            // parameter value, not the parameter name.
+            $key = strtoupper(trim($tmp[0]));
+
+            // get the parameter name by checking to see if it's in
+            // vCard 2.1 or 3.0 format.
+            $name = $this->_getParamName($key);
+
+            // list of all parameter values
+            $listall = array_key_exists(1, $tmp) ? trim($tmp[1]) : '';
+
+            // if there is a value-list for this parameter, they are
+            // separated by commas, so split them out too.
+            $list = $this->_splitByComma($listall);
+
+            // now loop through each value in the parameter and retain
+            // it.  if the value is blank, that means it's a 2.1-style
+            // param, and the key itself is the value.
+            foreach ($list as $val) {
+                if (trim($val) != '') {
+                    // 3.0 formatted parameter
+                    $params[$name][] = trim($val);
+                } else {
+                    // 2.1 formatted parameter
+                    $params[$name][] = $key;
+                }
+            }
+
+            // if, after all this, there are no parameter values for the
+            // parameter name, retain no info about the parameter (saves
+            // ram and checking-time later).
+            if (count($params[$name]) == 0) {
+                unset($params[$name]);
+            }
+        }
+        // return the parameters array.
+        return $params;
+    }
+
+    /**
+    * Returns the parameter name for parameters given without names.
+    *
+    * The vCard 2.1 specification allows parameter values without a
+    * name. The parameter name is then determined from the unique
+    * parameter value.
+    *
+    * Shamelessly lifted from Frank Hellwig  and his
+    * vCard PHP project .
+    *
+    * @param string $value The first element in a parameter name-value
+    * pair.
+    *
+    * @return string The proper parameter name (TYPE, ENCODING, or
+    * VALUE).
+    */
+    protected function _getParamName($value)
+    {
+        static $types = array (
+            'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
+            'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER',
+            'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
+            'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD',
+            'INTERNET', 'IBMMAIL', 'MCIMAIL',
+            'POWERSHARE', 'PRODIGY', 'TLX', 'X400',
+            'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB',
+            'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME',
+            'MPEG', 'MPEG2', 'AVI',
+            'WAVE', 'AIFF', 'PCM',
+            'X509', 'PGP'
+        );
+
+        // CONTENT-ID added by pmj
+        static $values = array (
+            'INLINE', 'URL', 'CID', 'CONTENT-ID'
+        );
+
+        // 8BIT added by pmj
+        static $encodings = array (
+            '7BIT', '8BIT', 'QUOTED-PRINTABLE', 'BASE64'
+        );
+
+        // changed by pmj to the following so that the name defaults to
+        // whatever the original value was.  Frank Hellwig's original
+        // code was "$name = 'UNKNOWN'".
+        $name = $value;
+
+        if (in_array($value, $types)) {
+            $name = 'TYPE';
+        } elseif (in_array($value, $values)) {
+            $name = 'VALUE';
+        } elseif (in_array($value, $encodings)) {
+            $name = 'ENCODING';
+        }
+
+        return $name;
+    }
+
+    /**
+    * Looks at a line's parameters;
+    * if ENCODING parameter is set and is QUOTED-PRINTABLE
+    *     then decode the text in-place.
+    * This method is overwritten in Parse/Vcard.php
+    *
+    * @param array  $params A parameter array from a vCard line.
+    * @param string $text   A right-part (after-the-colon part) from a line.
+    * @param string $prop   The property name.. passed so may have special-cases
+    *
+    * @return array
+    * @access private
+    * @uses quoted_printable_decode()
+    */
+    protected function _decode(array $params, $text,$prop=null)
+    {
+        // loop through each parameter
+        foreach ( $params as $param => $param_vals ) {
+            // check to see if it's an encoding param
+            if ( trim(strtoupper($param)) != 'ENCODING' ) {
+                continue;
+            }
+            // loop through each ENCODING param value
+            foreach ( $param_vals as $k => $param_val ) {
+                // if any of the values are QP, decode the text in-place and return
+                if ( trim(strtoupper($param_val)) == 'QUOTED-PRINTABLE' ) {
+                    $text = quoted_printable_decode($text);
+                    // remove the encoding param.. as it's no longer encoded!
+                    unset($params[$param][$k]);
+                    break;
+                }
+            }
+        }
+        return array($params, $text);
+    }
+
+    /**
+    * Convert text to the specified charaster set
+    *
+    * @param array  $params  A parameter array from a vCard line.
+    * @param string $text    A right-part (after-the-colon part) from a line.
+    * @param string $charset The charset to convert to
+    *
+    * @return array
+    * @access private
+    */
+    protected function _charset_conv(array $params, $text, $charset='UTF-8')
+    {
+        /*
+        vcard 2.1 default charset = ASCII.
+        vcard 3.0 default charset = UTF-8 // there is no CHARSET parameter
+        */
+        $charset_is = 'UTF-8';
+        foreach ( $params as $param => $param_vala ) {
+            if ( trim(strtoupper($param)) == 'CHARSET' ) {
+                $charset_is = $param_vala[0];
+                break;
+            }
+        }
+        if ( $charset_is == 'ASCII' && $charset == 'UTF-8' ) {
+            $charset_is = 'UTF-8';
+            // no conversion necessary
+            // unset($params['CHARSET']);
+        } elseif ( $charset_is != $charset ) {
+            if ( extension_loaded('mbstring') ) {
+                $text = mb_convert_encoding($text, $charset, $charset_is);
+                $params['CHARSET'] = array( $charset );
+            } else {
+                $text_new = htmlentities($text, ENT_NOQUOTES, $charset_is);
+                $text_new = html_entity_decode($text_new, ENT_QUOTES, $charset);
+                if ( $text_new ) {
+                    $text = $text_new;
+                    $params['CHARSET'] = array( $charset );
+                }
+            }
+        }
+        return array($params, $text);
+    }
 
 }
 
-?>
\ No newline at end of file
+?>

From c47220453309e8513632aa9f8f8bc7ba62acb593 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Thu, 4 Apr 2013 16:02:52 -0500
Subject: [PATCH 14/17] Update Vcard.php

after running phpCS
---
 File/IMC/Parse/Vcard.php | 351 +++++++++++++++++++--------------------
 1 file changed, 175 insertions(+), 176 deletions(-)

diff --git a/File/IMC/Parse/Vcard.php b/File/IMC/Parse/Vcard.php
index be28303..5394465 100644
--- a/File/IMC/Parse/Vcard.php
+++ b/File/IMC/Parse/Vcard.php
@@ -1,5 +1,4 @@
 ';
  * 
  *
- * @author Paul M. Jones 
+ * @author  Paul M. Jones 
  *
  * @package File_IMC
  *
@@ -74,186 +73,186 @@
 class File_IMC_Parse_Vcard extends File_IMC_Parse
 {
     /**
-	 * Return version.
-	 *
-	 * @uses parent::$data
-	 */
-	public function getVersion()
-	{
-		return $this->data['VCARD'][0]['VERSION'][0]['value'][0][0];
-	}
+     * Return version.
+     *
+     * @uses parent::$data
+     * @return string
+     */
+    public function getVersion()
+    {
+        return $this->data['VCARD'][0]['VERSION'][0]['value'][0][0];
+    }
 
-	/**
-	* Looks at a line's parameters;
-	* if ENCODING parameter is set -> decode the text in-place.
-	*
-	* @param array $params A parameter array from a vCard line.
-	* @param string $text A right-part (after-the-colon part) from a line.
-	* @param string $prop The property name.. passed so may have special-cases
-	* @return array
-	*
-	* @uses quoted_printable_decode()
-	* @uses base64_decode()
-	*/
-	protected function _decode(array $params, $text, $prop=null)
-	{
-		if ( in_array($prop,array('PHOTO','LOGO','SOUND','KEY')) ) {
-			// don't decode binary values
-			return array($params, $text);
-		}
-		// loop through each parameter
-		foreach ( $params as $param => $param_vals ) {
-			// check to see if it's an encoding param
-			if ( trim(strtoupper($param)) != 'ENCODING' ) {
-				continue;
-			}
-			// loop through each encoding param value
-			foreach ( $param_vals as $k => $param_val ) {
-				$param_val == trim(strtoupper($param_val));
-				if ( $param_val == 'QUOTED-PRINTABLE' ) {
-					$text = quoted_printable_decode($text);
-					// remove the encoding param.. as it's no longer encoded!
-					unset($params[$param][$k]);
-					break;
-				}
-				elseif ( in_array($param_val,array('BASE64','B')) ) {
-					$text = base64_decode($text);
-					// remove the encoding param.. as it's no longer encoded!
-					unset($params[$param][$k]);
-					break;
-				}
-			}
-		}
-		return array($params, $text);
-	}
+    /**
+    * Looks at a line's parameters;
+    * if ENCODING parameter is set -> decode the text in-place.
+    *
+    * @param array  $params A parameter array from a vCard line.
+    * @param string $text   A right-part (after-the-colon part) from a line.
+    * @param string $prop   The property name.. passed so may have special-cases
+    *
+    * @return array
+    *
+    * @uses quoted_printable_decode()
+    * @uses base64_decode()
+    */
+    protected function _decode(array $params, $text, $prop=null)
+    {
+        if ( in_array($prop, array('PHOTO','LOGO','SOUND','KEY')) ) {
+            // don't decode binary values
+            return array($params, $text);
+        }
+        // loop through each parameter
+        foreach ( $params as $param => $param_vals ) {
+            // check to see if it's an encoding param
+            if ( trim(strtoupper($param)) != 'ENCODING' ) {
+                continue;
+            }
+            // loop through each encoding param value
+            foreach ( $param_vals as $k => $param_val ) {
+                $param_val == trim(strtoupper($param_val));
+                if ( $param_val == 'QUOTED-PRINTABLE' ) {
+                    $text = quoted_printable_decode($text);
+                    // remove the encoding param.. as it's no longer encoded!
+                    unset($params[$param][$k]);
+                    break;
+                } elseif ( in_array($param_val, array('BASE64','B')) ) {
+                    $text = base64_decode($text);
+                    // remove the encoding param.. as it's no longer encoded!
+                    unset($params[$param][$k]);
+                    break;
+                }
+            }
+        }
+        return array($params, $text);
+    }
 
-	/**
-	*
-	* Parses a vCard line value identified as being of the "N"
-	* (structured name) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return array An array of key-value pairs where the key is the
-	* portion-name and the value is the portion-value.  The value itself
-	* may be an array as well if multiple comma-separated values were
-	* indicated in the vCard source.
-	*
-	* @see parent::_splitBySemi()
-	* @see parent::_splitByComma()
-	*/
-	protected function _parseN($text)
-	{
-		// array_pad makes sure there are the right number of elements
-		$tmp = array_pad($this->_splitBySemi($text), 5, '');
-		return array(
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_N_FAMILY]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_N_GIVEN]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_N_ADDL]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_N_PREFIX]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_N_SUFFIX])
-		);
-	}
+    /**
+    * Parses a vCard line value identified as being of the "N"
+    * (structured name) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    * @see parent::_splitBySemi()
+    * @see parent::_splitByComma()
+    */
+    protected function _parseN($text)
+    {
+        // array_pad makes sure there are the right number of elements
+        $tmp = array_pad($this->_splitBySemi($text), 5, '');
+        return array(
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_N_FAMILY]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_N_GIVEN]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_N_ADDL]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_N_PREFIX]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_N_SUFFIX])
+        );
+    }
 
-	/**
-	* Parses a vCard line value identified as being of the "ADR"
-	* (structured address) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return array An array of key-value pairs where the key is the
-	* portion-name and the value is the portion-value.  The value itself
-	* may be an array as well if multiple comma-separated values were
-	* indicated in the vCard source.
-	*
-	*/
-	protected function _parseADR($text)
-	{
-		// array_pad makes sure there are the right number of elements
-		$tmp = array_pad($this->_splitBySemi($text), 7, '');
-		return array(
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_POB]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_EXTEND]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_STREET]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_LOCALITY]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_REGION]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_POSTCODE]),
-			$this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_COUNTRY])
-		);
-	}
+    /**
+    * Parses a vCard line value identified as being of the "ADR"
+    * (structured address) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    */
+    protected function _parseADR($text)
+    {
+        // array_pad makes sure there are the right number of elements
+        $tmp = array_pad($this->_splitBySemi($text), 7, '');
+        return array(
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_POB]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_EXTEND]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_STREET]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_LOCALITY]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_REGION]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_POSTCODE]),
+            $this->_splitByComma($tmp[FILE_IMC::VCARD_ADR_COUNTRY])
+        );
+    }
 
-	/**
-	* Parses a vCard line value identified as being of the "NICKNAME"
-	* (informal or descriptive name) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return array An array of nicknames.
-	*
-	*/
-	protected function _parseNICKNAME($text)
-	{
-		return array($this->_splitByComma($text));
-	}
+    /**
+    * Parses a vCard line value identified as being of the "NICKNAME"
+    * (informal or descriptive name) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return array An array of nicknames.
+    *
+    */
+    protected function _parseNICKNAME($text)
+    {
+        return array($this->_splitByComma($text));
+    }
 
-	/**
-	* Parses a vCard line value identified as being of the "ORG"
-	* (organizational info) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return array An array of organizations; each element of the array
-	* is itself an array, which indicates primary organization and
-	* sub-organizations.
-	*
-	*/
-	protected function _parseORG($text)
-	{
-		$tmp = $this->_splitbySemi($text);
-		$list = array();
-		foreach ($tmp as $val) {
-			$list[] = array($val);
-		}
-		return $list;
-	}
+    /**
+    * Parses a vCard line value identified as being of the "ORG"
+    * (organizational info) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return array An array of organizations; each element of the array
+    * is itself an array, which indicates primary organization and
+    * sub-organizations.
+    *
+    */
+    protected function _parseORG($text)
+    {
+        $tmp = $this->_splitbySemi($text);
+        $list = array();
+        foreach ($tmp as $val) {
+            $list[] = array($val);
+        }
+        return $list;
+    }
 
-	/**
-	* Parses a vCard line value identified as being of the "CATEGORIES"
-	* (card-category) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return mixed An array of categories.
-	*
-	*/
-	protected function _parseCATEGORIES($text)
-	{
-		return array($this->_splitByComma($text));
-	}
+    /**
+    * Parses a vCard line value identified as being of the "CATEGORIES"
+    * (card-category) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return mixed An array of categories.
+    *
+    */
+    protected function _parseCATEGORIES($text)
+    {
+        return array($this->_splitByComma($text));
+    }
 
-	/**
-	* Parses a vCard line value identified as being of the "GEO"
-	* (geographic coordinate) type-defintion.
-	*
-	* @param string $text The right-part (after-the-colon part) of a
-	* vCard line.
-	*
-	* @return mixed An array of lat-lon geocoords.
-	*/
-	protected function _parseGEO($text)
-	{
-		// array_pad makes sure there are the right number of elements
-		$tmp = array_pad($this->_splitBySemi($text), 2, '');
-		return array(
-			array($tmp[FILE_IMC::VCARD_GEO_LAT]), // lat
-			array($tmp[FILE_IMC::VCARD_GEO_LON])  // lon
-		);
-	}
+    /**
+    * Parses a vCard line value identified as being of the "GEO"
+    * (geographic coordinate) type-defintion.
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    *
+    * @return mixed An array of lat-lon geocoords.
+    */
+    protected function _parseGEO($text)
+    {
+        // array_pad makes sure there are the right number of elements
+        $tmp = array_pad($this->_splitBySemi($text), 2, '');
+        return array(
+            array($tmp[FILE_IMC::VCARD_GEO_LAT]), // lat
+            array($tmp[FILE_IMC::VCARD_GEO_LON])  // lon
+        );
+    }
 }
 
-?>
\ No newline at end of file
+?>

From 3caf72d700bca4f5f5ed346228597a1fb5d02bf8 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Thu, 4 Apr 2013 16:04:00 -0500
Subject: [PATCH 15/17] Update Vcard.php

after running phpCS
---
 File/IMC/Build/Vcard.php | 2689 ++++++++++++++++++++++----------------
 1 file changed, 1550 insertions(+), 1139 deletions(-)

diff --git a/File/IMC/Build/Vcard.php b/File/IMC/Build/Vcard.php
index 0cd4845..41f9967 100644
--- a/File/IMC/Build/Vcard.php
+++ b/File/IMC/Build/Vcard.php
@@ -54,1145 +54,1556 @@
 */
 class File_IMC_Build_Vcard extends File_IMC_Build
 {
-	/**
-	* Constructor
-	*
-	* @param string $version The vCard version to build; affects which
-	* parameters are allowed and which properties are returned by
-	* fetch().
-	*
-	* @return File_IMC_Build_Vcard
-	*
-	* @see  parent::fetch()
-	* @uses parent::reset()
-	*/
-	public function __construct($version = '3.0')
-	{
-		$this->reset($version);
-	}
-
-	public function setVersion($val='3.0')
-	{
-		$this->set('VERSION',$val);
-	}
-
-	/**
-	* Validates parameter names and values based on the vCard version
-	* (2.1 or 3.0).
-	*
-	* @access public
-	* @param  string $name The parameter name (e.g., TYPE or ENCODING).
-	*
-	* @param  string $text The parameter value (e.g., HOME or BASE64).
-	*
-	* @param  string $prop Optional, the propety name (e.g., ADR or PHOTO).
-	*						Only used for error messaging.
-	*
-	* @param  int $iter Optional, the iteration of the property.
-	*						Only used for error messaging.
-	*
-	* @return mixed	Boolean true if the parameter is valid
-	* @throws File_IMC_Exception ... if not.
-	*
-	* @uses self::validateParam21()
-	* @uses self::validateParam30()
-	*/
-	public function validateParam($name, $text, $prop=null, $iter=null)
-	{
-		$name = strtoupper($name);
-		$text = strtoupper($text);
-		// all param values must have only the characters A-Z 0-9 and -.
-		if (preg_match('/[^a-zA-Z0-9\-]/i', $text)) {
-			throw new File_IMC_Exception(
-				"vCard [$prop] [$iter] [$name]: The parameter value may contain only a-z, A-Z, 0-9, and dashes (-).",
-				FILE_IMC::ERROR_INVALID_PARAM);
-		}
-		if ( $this->value['VERSION'][0][0][0] == '2.1' ) {
-			return $this->_validateParam21($name, $text, $prop, $iter);
-		} elseif ( $this->value['VERSION'][0][0][0] == '3.0' ) {
-			return $this->_validateParam30($name, $text, $prop, $iter);
-		}
-		throw new File_IMC_Exception(
-			"[$prop] [$iter] Unknown vCard version number or other error.",
-			FILE_IMC::ERROR);
-	}
-
-	/**
-	 * Validate parameters with 2.1 vcards.
-	 *
-	 * @access private
-	 * @param string $name The parameter name (e.g., TYPE or ENCODING).
-	 * @param string $text The parameter value (e.g., HOME or BASE64).
-	 * @param string $prop the property name (e.g., ADR or PHOTO).
-	 *						Only used for error messaging.
-	 * @param int $iter Optional, the iteration of the property.
-	 *						Only used for error messaging.
-	 * @return boolean
-	 */
-	protected function _validateParam21($name, $text, $prop, $iter)
-	{
-		// Validate against version 2.1 (pretty strict)
-		$x_val = strpos($text,'X-') === 0;
-		switch ($name) {
-		case 'TYPE':
-			static $types = array (
-				// ADR
-				'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
-				// TEL
-				'PREF','VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
-				//EMAIL
-				'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD','INTERNET',
-					'IBMMAIL', 'MCIMAIL','POWERSHARE', 'PRODIGY', 'TLX', 'X400',
-				//PHOTO & LOGO
-				'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB', 'PICT', 'TIFF',
-					'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME',
-				//SOUND
-				'WAVE', 'AIFF', 'PCM',
-				// KEY
-				'X509', 'PGP'
-			);
-			$result = ( in_array($text, $types) || $x_val );
-			break;
-		case 'ENCODING':
-			$vals = array('7BIT','8BIT','BASE64','QUOTED-PRINTABLE');
-			$result = ( in_array($text, $vals) || $x_val );
-			break;
-		case 'CHARSET':  // all charsets are OK
-		case 'LANGUAGE': // all languages are OK
-			$result = true;
-			break;
-		case 'VALUE':
-			$vals = array('INLINE','CONTENT-ID','CID','URL','VCARD');
-			$result = ( in_array($text, $vals) || $x_val );
-			break;
-		default:
-			$result = ( strpos($name,'X-') === 0 );
-			/*
-			if ( !$result )
-				throw new File_IMC_Exception(
-					'vCard 2.1 ['.$prop.']['.$iter.']: "'.$name.'" is an unknown or invalid parameter name.',
-					FILE_IMC::ERROR_INVALID_PARAM);
-			*/
-			break;
-		}
-		/*
-		if ( !$result )
-			throw new File_IMC_Exception(
-				'vCard 2.1 ['.$prop.']['.$iter.']: "'.$text.'" is not a recognized '.$name.' value.',
-				FILE_IMC::ERROR_INVALID_PARAM);
-		*/
-		return $result;
-	}
-
-	/**
-	 * Validate parameters with 3.0 vcards.
-	 *
-	 * @access private
-	 * @param string $name The parameter name (e.g., TYPE or ENCODING).
-	 * @param string $text The parameter value (e.g., HOME or BASE64).
-	 * @param string $prop the property name (e.g., ADR or PHOTO).
-	 *						Only used for error messaging.
-	 * @param int $iter the iteration of the property.
-	 *						Only used for error messaging.
-	 * @return boolean
-	 */
-	protected function _validateParam30($name, $text, $prop, $iter)
-	{
-		// Validate against version 3.0 (pretty lenient)
-		$x_val = strpos($text,'X-') === 0;
-		switch ($name) {
-		case 'TYPE':     // all types are OK
-		case 'LANGUAGE': // all languages are OK
-			$result = true;
-			break;
-		case 'ENCODING':
-			$vals = array('8BIT','B');
-			$result = ( in_array($text, $vals) || $x_val );
-			break;
-		case 'VALUE':
-			$vals = array('BINARY','PHONE-NUMBER','TEXT','URI','UTC-OFFSET','VCARD');
-			$result = ( in_array($text, $vals) || $x_val );
-			break;
-		default:
-			$result = ( strpos($name,'X-') === 0 );
-			/*
-			if ( !$result )
-				throw new File_IMC_Exception(
-					'vCard 3.0 ['.$prop.']['.$iter.']: "'.$name.'" is an unknown or invalid parameter name.',
-					FILE_IMC::ERROR_INVALID_PARAM);
-			*/
-			break;
-		}
-		/*
-		if ( !$result )
-			throw new File_IMC_Exception(
-				'vCard 3.0 ['.$prop.']['.$iter.']: "'.$text.'" is not a recognized '.$name.' value.',
-				FILE_IMC::ERROR_INVALID_PARAM);
-		*/
-		return $result;
-	}
-
-	/**
-	* Sets the value of one entire ADR iteration.
-	*
-	* @access private
-	* @param array address components
-	*   post-office-box
-	*   extended-address
-	*   street-address
-	*   locality		: (e.g., city)
-	*   region			: (e.g., state, province, or governorate)
-	*   postal-code		: (e.g., ZIP code)
-	*	country-name
-	*  value may be passed as a numeric or key/value array
-	*  (keys coming from hCard microformat specification)
-	*  each component may be a String (one repetition) or array (multiple reptitions)
-	* @param int iteration
-	* @return $this
-	*/
-	protected function _setADR($value, $iter)
-	{
-		$keys = array(
-			'post-office-box',
-			'extended-address',
-			'street-address',
-			'locality',
-			'region',
-			'postal-code',
-			'country-name',
-		);
-		foreach ( $keys as $i => $k )
-		{
-			if ( isset($value[$k]) )
-				$value[$i] = $value[$k];
-			if ( !isset($value[$i]) )
-				$value[$i] = '';
-		}
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_POB,       $value[0]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_EXTEND,    $value[1]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_STREET,    $value[2]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_LOCALITY,  $value[3]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_REGION,    $value[4]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_POSTCODE,  $value[5]);
-		$this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_COUNTRY,   $value[6]);
-	}
-
-	/**
-	* Sets the FN property of the card.  If no text is passed as the
-	* FN value, constructs an FN automatically from N property.
-	*
-	* @access private
-	* @param string $text Override the automatic generation of FN from N
-	*    elements with the specified text.
-	* @return mixed Void on success
-	* @throws File_IMC_Exception ... on failure.
-	*/
-	protected function _setFN($text=null, $iter)
-	{
-		if ( $text === null ) {
-			// no text was specified for the FN, so build it
-			// from the current N property if an N exists
-			if ( is_array($this->value['N']) ) {
-				// build from N.
-				// first (given) name, first iteration, first repetition
-				$text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_GIVEN, 0);
-				// add a space after, if there was text
-				if ($text != '') {
-					$text .= ' ';
-				}
-				// last (family) name, first iteration, first repetition
-				$text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_FAMILY, 0);
-				// add a space after, if there was text
-				if ($text != '') {
-					$text .= ' ';
-				}
-				// last-name suffix, first iteration, first repetition
-				$text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_SUFFIX, 0);
-			} else {
-				// no N exists, and no FN was set, so return.
-				throw new File_IMC_Exception('FN not specified and N not set; cannot set FN.',FILE_IMC::ERROR_PARAM_NOT_SET);
-			}
-		}
-		$this->setValue('FN', $iter, 0, $text);
-	}
-
-	/**
-	* Sets the GEO property (both latitude and longitude)
-	*
-	* @access private
-	* @param array coords lat and lon
-	*     value may be passed as a numeric or key/value array
-	*     (keys coming from geo microformat specification)
-	* @param int iteration
-	* @return $this
-	*/
-	protected function _setGEO($value, $iter)
-	{
-		$keys = array(
-			'latitude',
-			'longitude',
-		);
-		foreach ( $keys as $i => $k )
-		{
-			if ( isset($value[$k]) )
-				$value[$i] = $value[$k];
-			if ( !isset($value[$i]) )
-				$value[$i] = '';
-		}
-		$this->setValue('GEO', $iter, FILE_IMC::VCARD_GEO_LAT, $value[0]);
-		$this->setValue('GEO', $iter, FILE_IMC::VCARD_GEO_LON, $value[1]);
-	}
-
-	/**
-	* Sets the full N property of the vCard.
-	*
-	* @access private
-	* @param array $value name comonents
-	*	family-name		: family/last name.
-	*	given-name		: given/first name.
-	*	additional-name	: additional/middle name.
-	*	honorific-prefix: prefix such as Mr., Miss, etc.
-	*	honorific-suffix: suffix such as III, Jr., Ph.D., etc.
-	* value may be passed as a numeric or key/value array
-	*   (keys coming from hCard microformat specification)
-	* each component may be a string or array
-	*/
-	protected function _setN($value,$iter)
-	{
-		$keys = array(
-			'family-name',
-			'given-name',
-			'additional-name',
-			'honorific-prefix',
-			'honorific-suffix',
-		);
-		foreach ( $keys as $i => $k )
-		{
-			if ( isset($value[$k]) )
-				$value[$i] = $value[$k];
-			if ( !isset($value[$i]) )
-				$value[$i] = '';
-		}
-		$this->setValue('N', $iter, FILE_IMC::VCARD_N_FAMILY,	$value[0]);
-		$this->setValue('N', $iter, FILE_IMC::VCARD_N_GIVEN,	$value[1]);
-		$this->setValue('N', $iter, FILE_IMC::VCARD_N_ADDL,		$value[2]);
-		$this->setValue('N', $iter, FILE_IMC::VCARD_N_PREFIX,	$value[3]);
-		$this->setValue('N', $iter, FILE_IMC::VCARD_N_SUFFIX,	$value[4]);
-	}
-
-	/**
-	* Sets the full value of the ORG property.
-	*
-	* The ORG property can have one or more parts (as opposed to
-	* repetitions of values within those parts).  The first part is the
-	* highest-level organization, the second part is the next-highest,
-	* the third part is the third-highest, and so on.  There can by any
-	* number of parts in one ORG iteration.  (This is different from
-	* other properties, such as NICKNAME, where an iteration has only
-	* one part but may have many repetitions within that part.)
-	*
-	* @access private
-	* @param mixed $value String (one ORG part) or array (of ORG parts)
-	*     to use as the value for the property iteration.
-	* @param int iteration
-	*/
-	protected function _setORG($value,$iter)
-	{
-		$keys = array(
-			'organization-name',
-			'organization-unit',	// may pass an array
-		);
-		settype($value, 'array');
-		foreach ( $keys as $i => $k )
-		{
-			if ( isset($value[$k]) )
-			{
-				$value[] = $value[$k];
-				unset($value[$k]);
-			}
-		}
-		// flatten the array
-		$vals = $value;
-		$value = array();
-		foreach ( $vals as $v )
-		{
-			settype($v,'array');
-			$value = array_merge($value,$v);
-		}
-		// clear existing value
-		if ( isset($this->value['ORG'][$iter]) ) {
-			unset($this->value['ORG'][$iter]);
-		}
-		// set the new value(s)
-		foreach ( $value as $k => $v) {
-			settype($v, 'array');
-			foreach ( $v as $v2 ) {
-				if ( !empty($v2) )
-					$this->setValue('ORG', $iter, $k, $v2);
-			}
-		}
-	}
-
-	/**
-	* Gets back the value of one ADR property iteration.
-	*
-	* @access private
-	* @param int $iter The property iteration-number to get the value for.
-	* @return mixed The value of this property iteration, or ...
-	* @throws File_IMC_Exception ... if the iteration is not valid.
-	*/
-	protected function _getADR($iter)
-	{
-		if (! is_integer($iter) || $iter < 0) {
-			throw new File_IMC_Exception(
-				'ADR iteration number not valid.',
-				FILE_IMC::ERROR_INVALID_ITERATION);
-		}
-		return $this->getMeta('ADR', $iter)
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_POB) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_EXTEND) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_STREET) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_LOCALITY) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_REGION) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_POSTCODE) . ';'
-			.$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_COUNTRY);
-	}
-
-	/**
-	* Gets back the value of the GEO property.
-	*
-	* @access private
-	* @param int $iter The property iteration-number to get
-	* @return string The value of this property.
-	*/
-	protected function _getGEO($iter)
-	{
-		return $this->getMeta('GEO', $iter)
-			.$this->getValue('GEO', $iter, FILE_IMC::VCARD_GEO_LAT, 0) . ';'
-			.$this->getValue('GEO', $iter, FILE_IMC::VCARD_GEO_LON, 0);
-	}
-
-	/**
-	* Gets back the full N property
-	*
-	* @access private
-	* @param int $iter The property iteration-number to get the value for.
-	* @return string
-	*/
-	protected function _getN($iter)
-	{
-		return $this->getMeta('N', $iter)
-			.$this->getValue('N', $iter, FILE_IMC::VCARD_N_FAMILY) . ';'
-			.$this->getValue('N', $iter, FILE_IMC::VCARD_N_GIVEN) . ';'
-			.$this->getValue('N', $iter, FILE_IMC::VCARD_N_ADDL) . ';'
-			.$this->getValue('N', $iter, FILE_IMC::VCARD_N_PREFIX) . ';'
-			.$this->getValue('N', $iter, FILE_IMC::VCARD_N_SUFFIX);
-	}
-
-	/**
-	* Gets back the value of the ORG property.
-	*
-	* @access private
-	* @param int $iter The property iteration-number to get the value for.
-	* @return string The value of this property.
-	*/
-	protected function _getORG($iter)
-	{
-		$text	= $this->getMeta('ORG', $iter);
-		$parts	= count($this->value['ORG'][$iter]);
-		$last = $parts - 1;
-		for ( $part = 0; $part < $parts; $part++ ) {
-			$text .= $this->getValue('ORG', $iter, $part);
-			if ( $part != $last ) {
-				$text .= ';';
-			}
-		}
-		return $text;
-	}
-
-	/**
-	* Sets the value of the specified property
-	*   for PHOTO, LOGO, SOUND, & KEY properties:
-	*		if a filepath is passed:, automatically base64-encodes
-	*			and sets ENCODING parameter
-	*		if a URL is passed, automatically sets the VALUE=URL|URI parameter
-	*
-	* _setPROPERTY($value,$iter) method will be used if exists  ( ie _setADR() )
-	*
-	* @access public
-	*
-	* @param string property
-	* @param mixed value
-	*       when property is ADR, GEO, or N:  value is an array
-	*			additionaly, the array may be an associateive array
-	*			ADR: 	post-office-box, extended-address, street-address, locality, region, postal-code, country-name
-	*			GEO:	latitude, longitude
-	*			N:		family-name, given-name, additional-name, honorific-prefix, honorific-suffix
-	*       when property is ORG, value may be an string or array
-	*			ORG		'organization-name','organization-unit'
-	*       for all other properties, value is a string
-	* @param mixed iteration default = 0; pass 'new' to add an iteration
-	* @return $this
-	*/
-	public function set($prop,$value,$iter=0)
-	{
-		$prop = strtoupper(trim($prop));
-		if ( $iter === 'new' )
-			$iter = isset($this->value[$prop])
-				? count($this->value[$prop])
-				: 0;
-		elseif ( !is_integer($iter) || $iter < 0) {
-			throw new File_IMC_Exception(
-				$prop.' iteration number not valid.', FILE_IMC::ERROR_INVALID_ITERATION
-			);
-		}
-		$method = '_set'.$prop;
-		if ( method_exists($this, $method) ) {
-			call_user_func(array($this,$method), $value, $iter);
-		}
-		else {
-			if ( $prop == 'VERSION' && !in_array($value,array('2.1','3.0')) )
-				throw new File_IMC_Exception('Version must be 3.0 or 2.1 to be valid.', FILE_IMC::ERROR_INVALID_VCARD_VERSION);
-			elseif ( in_array($prop,array('PHOTO','LOGO','SOUND','KEY')) ) {
-				if ( file_exists($value) )
-					$value = base64_encode(file_get_contents($value));
-			}
-			$this->setValue($prop, $iter, 0, $value);
-			if ( in_array($prop,array('PHOTO','LOGO','SOUND','KEY')) ) {
-				$ver = $this->getValue('VERSION');
-				if ( preg_match('#^(https?|ftp)://#',$value) ) {
-					$this->addParam('VALUE', $ver == '2.1' ? 'URL' : 'URI' );
-				}
-				else {
-					$this->addParam('ENCODING', $ver == '2.1' ? 'BASE64' : 'B' );
-				}
-			}
-		}
-		return $this;
-	}
-
-	/**
-	* Gets back the vcard line of the specified property (property name, params, & value)
-	*    this func removes the need for all the public getXxx functions...
-	*      uses the protected methods: _getADR, _getGEO, _getN, & _getORG
-	* If an encoding parameter has been specified, then it is assumed that the value has already
-	* If no encoding specified, the value will be encoded automatically as necessary
-	*
-	* _getPROPERTY($iter) method will be used if exists  ( ie _getADR() )
-	*
-	* @access public
-	*
-	* @param string property
-	* @param int iteration default = 0
-	* @return string The value of the property
-	*/
-	public function get($prop,$iter=0)
-	{
-		$return = '';
-		$prop = strtoupper(trim($prop));
-		if ( !is_integer($iter) || $iter < 0) {
-			throw new File_IMC_Exception(
-				$prop.' iteration number not valid.', FILE_IMC::ERROR_INVALID_ITERATION
-			);
-		}
-		$this->encode($prop,$iter);
-		$method = '_get'.$prop;
-		if ( method_exists($this, $method) )
-			$return = call_user_func(array($this,$method),$iter);
-		else
-			$return = $this->getMeta($prop, $iter) . $this->getValue($prop, $iter, 0);
-		return $return;
-	}
-
-	/**
-	* Fetches a full vCard text block based on $this->value and
-	* $this->param. The order of the returned properties is similar to
-	* their order in RFC 2426.  Honors the value of
-	* $this->value['VERSION'] to determine which vCard properties are
-	* returned (2.1- or 3.0-compliant).
-	*
-	* @access public
-	* @uses self::get()
-	*
-	* @return string A properly formatted vCard text block.
-	*/
-	public function fetch()
-	{
-		$prop_dfn_default = array(
-			'vers'	=> array('2.1','3.0'),
-			'req'	=> array(),				// versions required in
-			'limit'	=> false,				// just one value allowed
-		);
-		$prop_dfns = array(
-			'VERSION'	=> array( 'req' => array('2.1','3.0'),	'limit' => true ),
-			'FN'		=> array( 'req'	=> array('3.0') ),
-			'N'			=> array( 'req'	=> array('2.1','3.0') ),
-			'PROFILE'	=> array( 'vers'=> array('3.0'),		'limit' => true ),
-			'NAME'		=> array( 'vers'=> array('3.0'),		'limit' => true ),
-			'SOURCE'	=> array( 'vers'=> array('3.0'),		'limit' => true ),
-			'NICKNAME'	=> array( 'vers'=> array('3.0') ),
-			'PHOTO' 	=> array( ),
-			'BDAY'		=> array( ),
-			'ADR'		=> array( ),	// 'limit' => false
-			'LABEL'		=> array( ),	// 'limit' => false
-			'TEL'		=> array( ),	// 'limit' => false
-			'EMAIL'		=> array( ),	// 'limit' => false
-			'MAILER'	=> array( ),
-			'TZ'		=> array( ),
-			'GEO'		=> array( ),
-			'TITLE'		=> array( ),
-			'ROLE'		=> array( ),
-			'LOGO'		=> array( ),
-			'AGENT'		=> array( ),
-			'ORG'		=> array( ),
-			'CATEGORIES'=> array( 'vers' => array('3.0') ),
-			'NOTE'		=> array( ),
-			'PRODID'	=> array( 'vers' => array('3.0') ),
-			'REV'		=> array( ),
-			'SORT-STRING'=>array( 'vers' => array('3.0') ),
-			'SOUND'		=> array( ),
-			'UID'		=> array( ),
-			'URL'		=> array( ),
-			'CLASS'		=> array( 'vers' => array('3.0') ),
-			'KEY'		=> array( ),
-		);
-		$ver = $this->getValue('VERSION');
-		$newline = $ver == '2.1'
-			? "\r\n"	// version 2.1 uses \r\n for new lines
-			: "\n";		// version 3.0 uses \n
-
-		// initialize the vCard lines
-		$lines = array();
-
-		$lines[] = 'BEGIN:VCARD';
-
-		$prop_keys = array_keys($this->value);
-
-		foreach ( $prop_dfns as $prop => $prop_dfn ) {
-			if ( !is_array($prop_dfn) )
-				$prop_dfn = array( 'func' => $prop_dfn );
-			$prop_dfn = array_merge($prop_dfn_default,$prop_dfn);
-			if ( false !== $key = array_search($prop,$prop_keys) );
-				unset($prop_keys[$key]);
-			$prop_exists = isset($this->value[$prop]) && is_array($this->value[$prop]);
-			if ( $prop == 'PROFILE' && in_array($ver,$prop_dfn['vers']) )
-				$lines[] = 'PROFILE:VCARD';	// special case... don't really care what current val is
-			elseif ( $prop_exists ) {
-				if ( in_array($ver,$prop_dfn['vers']) ) {
-					foreach ( $this->value[$prop] as $iter => $val ) {
-						$lines[] = $this->get($prop,$iter);
-						if ( $prop_dfn['limit'] )
-							break;
-					}
-				}
-			}
-			elseif ( in_array($ver,$prop_dfn['req']) ) {
-				throw new File_IMC_Exception($prop.' not set (required).',FILE_IMC::ERROR_PARAM_NOT_SET);
-			}
-		}
-		// now build the extension properties
-		foreach ( $prop_keys as $prop ) {
-			if ( strpos($prop,'X-') === 0 ) {
-				foreach ($this->value[$prop] as $key => $val) {
-					$lines[] = $this->get($prop,$key);
-				}
-			}
-		}
-
-		$lines[] = 'END:VCARD';
-
-		// fold lines at 75 characters
-		$regex = '/(.{1,75})/i';
-		foreach ( $lines as $key => $val ) {
-			if (strlen($val) > 75) {
-				// we trim to drop the last newline, which will be added
-				// again by the implode function at the end of fetch()
-				$lines[$key] = trim(preg_replace($regex, "\\1$newline ", $val));
-			}
-		}
-
-		// compile the array of lines into a single text block and return
-		return implode($newline, $lines);
-	}
-
-	/********* deprecated methods *********/
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addAddress() {
-		$args = func_get_args();
-		return $this->set('ADR',$args,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addCategories($val) {
-		return $this->set('CATEGORIES',$val,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addEmail($val) {
-		return $this->set('EMAIL',$val,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addLabel($val) {
-		return $this->set('LABEL',$val,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addNickname($val) {
-		return $this->set('NICKNAME',$val,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addOrganization($val,$append=true) {
-		if ( $append && !empty($this->value['ORG'][0]) )
-		{
-			settype($val,'array');
-			$vals_cur = array();
-			foreach ( $this->value['ORG'][0] as $part_num => $part_val ) {
-				$vals_cur = array_merge($vals_cur,$part_val);
-			}
-			$val = array_merge($vals_cur,$val);
-		}
-		return $this->set('ORG',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function addTelephone($val) {
-		return $this->set('TEL',$val,'new');
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getAddress($iter=0) {
-		return $this->get('ADR',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getAgent($iter=0) {
-		return $this->get('AGENT',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getBirthday($iter=0) {
-		return $this->get('BDAY',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getCategories($iter=0) {
-		return $this->get('CATEGORIES',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getClass($iter=0) {
-		return $this->get('CLASS',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getEmail($iter=0) {
-		return $this->get('EMAIL',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getFormattedName($iter=0) {
-		return $this->get('FN',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getGeo($iter=0) {
-		return $this->get('GEO',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getKey($iter=0) {
-		return $this->get('KEY',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getLabel($iter=0) {
-		return $this->get('LABEL',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getLogo($iter=0) {
-		return $this->get('LOGO',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getMailer($iter=0) {
-		return $this->get('MAILER',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getName($iter=0) {
-		return $this->get('N',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getNickname($iter=0) {
-		return $this->get('NICKNAME',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getNote($iter=0) {
-		return $this->get('NOTE',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getOrganization($iter=0) {
-		return $this->get('ORG',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getPhoto($iter=0) {
-		return $this->get('PHOTO',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getProductID($iter=0) {
-		return $this->get('PRODID',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getRevision($iter=0) {
-		return $this->get('REV',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getRole($iter=0) {
-		return $this->get('ROLE',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getSortString($iter=0) {
-		return $this->get('SORT-STRING',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getSound($iter=0) {
-		return $this->get('SOUND',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getSource($iter=0) {
-		return $this->get('SOURCE',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getSourceName($iter=0) {
-		return $this->get('NAME',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getTZ($iter=0) {
-		return $this->get('TZ',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getTelephone($iter=0) {
-		return $this->get('TEL',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getTitle($iter=0) {
-		return $this->get('TITLE',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getURL($iter=0) {
-		return $this->get('URL',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::get()
-	*/
-	public function getUniqueID($iter=0) {
-		return $this->get('UID',$iter);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setAgent($val) {
-		return $this->set('AGENT',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setBirthday($val) {
-		return $this->set('BDAY',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setClass($val) {
-		return $this->set('CLASS',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setFormattedName($val) {
-		return $this->set('FN',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setGeo() {
-		$args = func_get_args();
-		return $this->set('GEO',$args);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setKey($val) {
-		return $this->set('KEY',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setLogo($val) {
-		return $this->set('LOGO',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setMailer($val) {
-		return $this->set('MAILER',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setName() {
-		$args = func_get_args();
-		return $this->set('N',$args);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setNote($val) {
-		return $this->set('NOTE',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setPhoto($val) {
-		return $this->set('PHOTO',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setProductID($val) {
-		return $this->set('PRODID',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setRevision($val) {
-		return $this->set('REV',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setRole($val) {
-		return $this->set('ROLE',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setSortString($val) {
-		return $this->set('SORT-STRING',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setSound($val) {
-		return $this->set('SOUND',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setSource($val) {
-		return $this->set('SOURCE',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setSourceName($val) {
-		return $this->set('NAME',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setTZ($val) {
-		return $this->set('TZ',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setTitle($val) {
-		return $this->set('TITLE',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setURL($val) {
-		return $this->set('URL',$val);
-	}
-
-	/**
-	* @deprecated
-	* @see self::set()
-	*/
-	public function setUniqueID($val) {
-		return $this->set('UID',$val);
-	}
+    /**
+    * Constructor
+    *
+    * @param string $version The vCard version to build; affects which
+    * parameters are allowed and which properties are returned by
+    * fetch().
+    *
+    * @return File_IMC_Build_Vcard
+    *
+    * @see  parent::fetch()
+    * @uses parent::reset()
+    */
+    public function __construct($version = '3.0')
+    {
+        $this->reset($version);
+    }
+
+    /**
+    * setVersion
+    *
+    * @param string $val version
+    *
+    * @return null
+    */
+    public function setVersion($val='3.0')
+    {
+        $this->set('VERSION', $val);
+    }
+
+    /**
+    * Validates parameter names and values based on the vCard version
+    * (2.1 or 3.0).
+    *
+    * @param string $name The parameter name (e.g., TYPE or ENCODING).
+    *
+    * @param string $text The parameter value (e.g., HOME or BASE64).
+    *
+    * @param string $prop Optional, the propety name (e.g., ADR or PHOTO).
+    *						Only used for error messaging.
+    *
+    * @param int    $iter Optional, the iteration of the property.
+    *						Only used for error messaging.
+    *
+    * @return mixed	Boolean true if the parameter is valid
+    * @access public
+    * @throws File_IMC_Exception ... if not.
+    *
+    * @uses self::validateParam21()
+    * @uses self::validateParam30()
+    */
+    public function validateParam($name, $text, $prop=null, $iter=null)
+    {
+        $name = strtoupper($name);
+        $text = strtoupper($text);
+        // all param values must have only the characters A-Z 0-9 and -.
+        if (preg_match('/[^a-zA-Z0-9\-]/i', $text)) {
+            throw new File_IMC_Exception(
+                "vCard [$prop] [$iter] [$name]: "
+                .'The parameter value may only contain '
+                .'a-z, A-Z, 0-9, and dashes (-).',
+                FILE_IMC::ERROR_INVALID_PARAM
+            );
+        }
+        if ( $this->value['VERSION'][0][0][0] == '2.1' ) {
+            return $this->_validateParam21($name, $text, $prop, $iter);
+        } elseif ( $this->value['VERSION'][0][0][0] == '3.0' ) {
+            return $this->_validateParam30($name, $text, $prop, $iter);
+        }
+        throw new File_IMC_Exception(
+            "[$prop] [$iter] Unknown vCard version number or other error.",
+            FILE_IMC::ERROR
+        );
+    }
+
+    /**
+     * Validate parameters with 2.1 vcards.
+     *
+     * @param string $name The parameter name (e.g., TYPE or ENCODING).
+     * @param string $text The parameter value (e.g., HOME or BASE64).
+     * @param string $prop the property name (e.g., ADR or PHOTO).
+     *						Only used for error messaging.
+     * @param int    $iter Optional, the iteration of the property.
+     *						Only used for error messaging.
+     *
+     * @return boolean
+     * @access private
+     */
+    protected function _validateParam21($name, $text, $prop, $iter)
+    {
+        // Validate against version 2.1 (pretty strict)
+        $x_val = strpos($text, 'X-') === 0;
+        switch ($name) {
+        case 'TYPE':
+            static $types = array (
+                // ADR
+                'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
+                // TEL
+                'PREF','VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS',
+                'MODEM', 'CAR', 'ISDN', 'VIDEO',
+                //EMAIL
+                'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD','INTERNET',
+                    'IBMMAIL', 'MCIMAIL','POWERSHARE', 'PRODIGY', 'TLX', 'X400',
+                //PHOTO & LOGO
+                'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB', 'PICT', 'TIFF',
+                    'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME',
+                //SOUND
+                'WAVE', 'AIFF', 'PCM',
+                // KEY
+                'X509', 'PGP'
+            );
+            $result = ( in_array($text, $types) || $x_val );
+            break;
+        case 'ENCODING':
+            $vals = array('7BIT','8BIT','BASE64','QUOTED-PRINTABLE');
+            $result = ( in_array($text, $vals) || $x_val );
+            break;
+        case 'CHARSET':  // all charsets are OK
+        case 'LANGUAGE': // all languages are OK
+            $result = true;
+            break;
+        case 'VALUE':
+            $vals = array('INLINE','CONTENT-ID','CID','URL','VCARD');
+            $result = ( in_array($text, $vals) || $x_val );
+            break;
+        default:
+            $result = ( strpos($name, 'X-') === 0 );
+            /*
+            if ( !$result )
+                throw new File_IMC_Exception(
+                    'vCard 2.1 ['.$prop.']['.$iter.']: '
+                    .'"'.$name.'" is an unknown or invalid parameter name.',
+                    FILE_IMC::ERROR_INVALID_PARAM);
+            */
+            break;
+        }
+        /*
+        if ( !$result )
+            throw new File_IMC_Exception(
+                'vCard 2.1 ['.$prop.']['.$iter.']: '
+                .'"'.$text.'" is not a recognized '.$name.' value.',
+                FILE_IMC::ERROR_INVALID_PARAM);
+        */
+        return $result;
+    }
+
+    /**
+     * Validate parameters with 3.0 vcards.
+     *
+     * @param string $name The parameter name (e.g., TYPE or ENCODING).
+     * @param string $text The parameter value (e.g., HOME or BASE64).
+     * @param string $prop the property name (e.g., ADR or PHOTO).
+     *						Only used for error messaging.
+     * @param int    $iter the iteration of the property.
+     *						Only used for error messaging.
+     *
+     * @return boolean
+     * @access private
+     */
+    protected function _validateParam30($name, $text, $prop, $iter)
+    {
+        // Validate against version 3.0 (pretty lenient)
+        $x_val = strpos($text, 'X-') === 0;
+        switch ($name) {
+        case 'TYPE':     // all types are OK
+        case 'LANGUAGE': // all languages are OK
+            $result = true;
+            break;
+        case 'ENCODING':
+            $vals = array('8BIT','B');
+            $result = ( in_array($text, $vals) || $x_val );
+            break;
+        case 'VALUE':
+            $vals = array('BINARY','PHONE-NUMBER','TEXT','URI','UTC-OFFSET','VCARD');
+            $result = ( in_array($text, $vals) || $x_val );
+            break;
+        default:
+            $result = ( strpos($name, 'X-') === 0 );
+            /*
+            if ( !$result )
+                throw new File_IMC_Exception(
+                    'vCard 3.0 ['.$prop.']['.$iter.']: '
+                    .'"'.$name.'" is an unknown or invalid parameter name.',
+                    FILE_IMC::ERROR_INVALID_PARAM);
+            */
+            break;
+        }
+        /*
+        if ( !$result )
+            throw new File_IMC_Exception(
+                'vCard 3.0 ['.$prop.']['.$iter.']: '
+                .'"'.$text.'" is not a recognized '.$name.' value.',
+                FILE_IMC::ERROR_INVALID_PARAM);
+        */
+        return $result;
+    }
+
+    /**
+    * Sets the value of one entire ADR iteration.
+    *
+    * @param array $value address components
+    *   post-office-box
+    *   extended-address
+    *   street-address
+    *   locality		: (e.g., city)
+    *   region			: (e.g., state, province, or governorate)
+    *   postal-code		: (e.g., ZIP code)
+    *	country-name
+    *  value may be passed as a numeric or key/value array
+    *  (keys coming from hCard microformat specification)
+    *  each component may be a String (one repetition) or array (multiple reptitions)
+    * @param int   $iter  iteration
+    *
+    * @return $this
+    * @access private
+    */
+    protected function _setADR($value, $iter)
+    {
+        $keys = array(
+            'post-office-box',
+            'extended-address',
+            'street-address',
+            'locality',
+            'region',
+            'postal-code',
+            'country-name',
+        );
+        foreach ( $keys as $i => $k ) {
+            if ( isset($value[$k]) ) {
+                $value[$i] = $value[$k];
+            }
+            if ( !isset($value[$i]) ) {
+                $value[$i] = '';
+            }
+        }
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_POB,       $value[0]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_EXTEND,    $value[1]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_STREET,    $value[2]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_LOCALITY,  $value[3]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_REGION,    $value[4]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_POSTCODE,  $value[5]);
+        $this->setValue('ADR', $iter, FILE_IMC::VCARD_ADR_COUNTRY,   $value[6]);
+    }
+
+    /**
+    * Sets the FN property of the card.  If no text is passed as the
+    * FN value, constructs an FN automatically from N property.
+    *
+    * @param string $text Override the automatic generation of FN from N
+    *    elements with the specified text.
+    * @param int    $iter Iteration
+    *
+    * @return mixed Void on success
+    * @access private
+    * @throws File_IMC_Exception ... on failure.
+    */
+    protected function _setFN($text=null, $iter=0)
+    {
+        if ( $text === null ) {
+            // no text was specified for the FN, so build it
+            // from the current N property if an N exists
+            if ( is_array($this->value['N']) ) {
+                // build from N.
+                // first (given) name, first iteration, first repetition
+                $text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_GIVEN, 0);
+                // add a space after, if there was text
+                if ($text != '') {
+                    $text .= ' ';
+                }
+                // last (family) name, first iteration, first repetition
+                $text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_FAMILY, 0);
+                // add a space after, if there was text
+                if ($text != '') {
+                    $text .= ' ';
+                }
+                // last-name suffix, first iteration, first repetition
+                $text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_SUFFIX, 0);
+            } else {
+                // no N exists, and no FN was set, so return.
+                throw new File_IMC_Exception(
+                    'FN not specified and N not set; cannot set FN.',
+                    FILE_IMC::ERROR_PARAM_NOT_SET
+                );
+            }
+        }
+        $this->setValue('FN', $iter, 0, $text);
+    }
+
+    /**
+    * Sets the GEO property (both latitude and longitude)
+    *
+    * @param array $value coords lat and lon
+    *     value may be passed as a numeric or key/value array
+    *     (keys coming from geo microformat specification)
+    * @param int   $iter  iteration
+    *
+    * @return $this
+    * @access private
+    */
+    protected function _setGEO($value, $iter)
+    {
+        $keys = array(
+            'latitude',
+            'longitude',
+        );
+        foreach ( $keys as $i => $k ) {
+            if ( isset($value[$k]) ) {
+                $value[$i] = $value[$k];
+            }
+            if ( !isset($value[$i]) ) {
+                $value[$i] = '';
+            }
+        }
+        $this->setValue('GEO', $iter, FILE_IMC::VCARD_GEO_LAT, $value[0]);
+        $this->setValue('GEO', $iter, FILE_IMC::VCARD_GEO_LON, $value[1]);
+    }
+
+    /**
+    * Sets the full N property of the vCard.
+    *
+    * @param array $value name comonents
+    *	family-name		: family/last name.
+    *	given-name		: given/first name.
+    *	additional-name	: additional/middle name.
+    *	honorific-prefix: prefix such as Mr., Miss, etc.
+    *	honorific-suffix: suffix such as III, Jr., Ph.D., etc.
+    * value may be passed as a numeric or key/value array
+    *   (keys coming from hCard microformat specification)
+    * each component may be a string or array
+    * @param int   $iter  iteration
+    *
+    * @return null
+    * @access private
+    */
+    protected function _setN($value,$iter)
+    {
+        $keys = array(
+            'family-name',
+            'given-name',
+            'additional-name',
+            'honorific-prefix',
+            'honorific-suffix',
+        );
+        foreach ( $keys as $i => $k ) {
+            if ( isset($value[$k]) ) {
+                $value[$i] = $value[$k];
+            }
+            if ( !isset($value[$i]) ) {
+                $value[$i] = '';
+            }
+        }
+        $this->setValue('N', $iter, FILE_IMC::VCARD_N_FAMILY,	$value[0]);
+        $this->setValue('N', $iter, FILE_IMC::VCARD_N_GIVEN,	$value[1]);
+        $this->setValue('N', $iter, FILE_IMC::VCARD_N_ADDL,		$value[2]);
+        $this->setValue('N', $iter, FILE_IMC::VCARD_N_PREFIX,	$value[3]);
+        $this->setValue('N', $iter, FILE_IMC::VCARD_N_SUFFIX,	$value[4]);
+    }
+
+    /**
+    * Sets the full value of the ORG property.
+    *
+    * The ORG property can have one or more parts (as opposed to
+    * repetitions of values within those parts).  The first part is the
+    * highest-level organization, the second part is the next-highest,
+    * the third part is the third-highest, and so on.  There can by any
+    * number of parts in one ORG iteration.  (This is different from
+    * other properties, such as NICKNAME, where an iteration has only
+    * one part but may have many repetitions within that part.)
+    *
+    * @param mixed $value String (one ORG part) or array (of ORG parts)
+    *     to use as the value for the property iteration.
+    * @param int   $iter  iteration
+    *
+    * @return null
+    * @access private
+    */
+    protected function _setORG($value,$iter)
+    {
+        $keys = array(
+            'organization-name',
+            'organization-unit',	// may pass an array
+        );
+        settype($value, 'array');
+        foreach ( $keys as $i => $k ) {
+            if ( isset($value[$k]) ) {
+                $value[] = $value[$k];
+                unset($value[$k]);
+            }
+        }
+        // flatten the array
+        $vals = $value;
+        $value = array();
+        foreach ( $vals as $v ) {
+            settype($v, 'array');
+            $value = array_merge($value, $v);
+        }
+        // clear existing value
+        if ( isset($this->value['ORG'][$iter]) ) {
+            unset($this->value['ORG'][$iter]);
+        }
+        // set the new value(s)
+        foreach ( $value as $k => $v) {
+            settype($v, 'array');
+            foreach ( $v as $v2 ) {
+                if ( !empty($v2) ) {
+                    $this->setValue('ORG', $iter, $k, $v2);
+                }
+            }
+        }
+    }
+
+    /**
+    * Gets back the value of one ADR property iteration.
+    *
+    * @param int $iter The property iteration-number to get the value for.
+    *
+    * @return mixed The value of this property iteration, or ...
+    * @access private
+    * @throws File_IMC_Exception ... if the iteration is not valid.
+    */
+    protected function _getADR($iter)
+    {
+        if (! is_integer($iter) || $iter < 0) {
+            throw new File_IMC_Exception(
+                'ADR iteration number not valid.',
+                FILE_IMC::ERROR_INVALID_ITERATION
+            );
+        }
+        return $this->getMeta('ADR', $iter)
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_POB) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_EXTEND) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_STREET) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_LOCALITY) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_REGION) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_POSTCODE) . ';'
+            .$this->getValue('ADR', $iter, FILE_IMC::VCARD_ADR_COUNTRY);
+    }
+
+    /**
+    * Gets back the value of the GEO property.
+    *
+    * @param int $iter The property iteration-number to get
+    *
+    * @return string The value of this property.
+    * @access private
+    */
+    protected function _getGEO($iter)
+    {
+        return $this->getMeta('GEO', $iter)
+            .$this->getValue('GEO', $iter, FILE_IMC::VCARD_GEO_LAT, 0) . ';'
+            .$this->getValue('GEO', $iter, FILE_IMC::VCARD_GEO_LON, 0);
+    }
+
+    /**
+    * Gets back the full N property
+    *
+    * @param int $iter The property iteration-number to get the value for.
+    *
+    * @return string
+    * @access private
+    */
+    protected function _getN($iter)
+    {
+        return $this->getMeta('N', $iter)
+            .$this->getValue('N', $iter, FILE_IMC::VCARD_N_FAMILY) . ';'
+            .$this->getValue('N', $iter, FILE_IMC::VCARD_N_GIVEN) . ';'
+            .$this->getValue('N', $iter, FILE_IMC::VCARD_N_ADDL) . ';'
+            .$this->getValue('N', $iter, FILE_IMC::VCARD_N_PREFIX) . ';'
+            .$this->getValue('N', $iter, FILE_IMC::VCARD_N_SUFFIX);
+    }
+
+    /**
+    * Gets back the value of the ORG property.
+    *
+    * @param int $iter The property iteration-number to get the value for.
+    *
+    * @return string The value of this property.
+    * @access private
+    */
+    protected function _getORG($iter)
+    {
+        $text	= $this->getMeta('ORG', $iter);
+        $parts	= count($this->value['ORG'][$iter]);
+        $last = $parts - 1;
+        for ( $part = 0; $part < $parts; $part++ ) {
+            $text .= $this->getValue('ORG', $iter, $part);
+            if ( $part != $last ) {
+                $text .= ';';
+            }
+        }
+        return $text;
+    }
+
+    /**
+    * Sets the value of the specified property
+    *   for PHOTO, LOGO, SOUND, & KEY properties:
+    *		if a filepath is passed:, automatically base64-encodes
+    *			and sets ENCODING parameter
+    *		if a URL is passed, automatically sets the VALUE=URL|URI parameter
+    *
+    * _setPROPERTY($value,$iter) method will be used if exists  ( ie _setADR() )
+    *
+    * @param string $prop  property
+    * @param mixed  $value value
+    *       when property is ADR, GEO, or N:  value is an array
+    *			additionaly, the array may be an associateive array
+    *			ADR: 	post-office-box, extended-address, street-address,
+    *                      locality, region, postal-code, country-name
+    *			GEO:	latitude, longitude
+    *			N:		family-name, given-name, additional-name,
+    *                      honorific-prefix, honorific-suffix
+    *       when property is ORG, value may be an string or array
+    *			ORG		'organization-name','organization-unit'
+    *       for all other properties, value is a string
+    * @param mixed  $iter  iteration default = 0; pass 'new' to add an iteration
+    *
+    * @return $this
+    * @access public
+    */
+    public function set($prop,$value,$iter=0)
+    {
+        $prop = strtoupper(trim($prop));
+        if ( $iter === 'new' ) {
+            $iter = isset($this->value[$prop])
+                ? count($this->value[$prop])
+                : 0;
+        } elseif ( !is_integer($iter) || $iter < 0) {
+            throw new File_IMC_Exception(
+                $prop.' iteration number not valid.',
+                FILE_IMC::ERROR_INVALID_ITERATION
+            );
+        }
+        $method = '_set'.$prop;
+        if ( method_exists($this, $method) ) {
+            call_user_func(array($this,$method), $value, $iter);
+        } else {
+            if ( $prop == 'VERSION' && !in_array($value, array('2.1','3.0')) ) {
+                throw new File_IMC_Exception(
+                    'Version must be 3.0 or 2.1 to be valid.',
+                    FILE_IMC::ERROR_INVALID_VCARD_VERSION
+                );
+            } elseif ( in_array($prop, array('PHOTO','LOGO','SOUND','KEY')) ) {
+                if ( file_exists($value) ) {
+                    $value = base64_encode(file_get_contents($value));
+                }
+            }
+            $this->setValue($prop, $iter, 0, $value);
+            if ( in_array($prop, array('PHOTO','LOGO','SOUND','KEY')) ) {
+                $ver = $this->getValue('VERSION');
+                if ( preg_match('#^(https?|ftp)://#', $value) ) {
+                    $this->addParam('VALUE', $ver == '2.1' ? 'URL' : 'URI');
+                } else {
+                    $this->addParam('ENCODING', $ver == '2.1' ? 'BASE64' : 'B');
+                }
+            }
+        }
+        return $this;
+    }
+
+    /**
+    * Gets back the vcard line of the specified property
+    *    (property name, params, & value)
+    * This func removes the need for all the public getXxx functions...
+    *      uses the protected methods: _getADR, _getGEO, _getN, & _getORG
+    *
+    * _getPROPERTY($iter) method will be used if exists  ( ie _getADR() )
+    *
+    * @param string $prop property
+    * @param int    $iter iteration default = 0
+    *
+    * @return string The value of the property
+    * @access public
+    */
+    public function get($prop,$iter=0)
+    {
+        $return = '';
+        $prop = strtoupper(trim($prop));
+        if ( !is_integer($iter) || $iter < 0) {
+            throw new File_IMC_Exception(
+                $prop.' iteration number not valid.',
+                FILE_IMC::ERROR_INVALID_ITERATION
+            );
+        }
+        $this->encode($prop, $iter);
+        $method = '_get'.$prop;
+        if ( method_exists($this, $method) ) {
+            $return = call_user_func(array($this,$method), $iter);
+        } else {
+            $return = $this->getMeta($prop, $iter).$this->getValue($prop, $iter, 0);
+        }
+        return $return;
+    }
+
+    /**
+    * Fetches a full vCard text block based on $this->value and
+    * $this->param. The order of the returned properties is similar to
+    * their order in RFC 2426.  Honors the value of
+    * $this->value['VERSION'] to determine which vCard properties are
+    * returned (2.1- or 3.0-compliant).
+    *
+    * @return string A properly formatted vCard text block.
+    *
+    * @access public
+    * @uses self::get()
+    */
+    public function fetch()
+    {
+        $prop_dfn_default = array(
+            'vers'	=> array('2.1','3.0'),
+            'req'	=> array(),				// versions required in
+            'limit'	=> false,				// just one value allowed
+        );
+        $prop_dfns = array(
+            'VERSION'	=> array( 'req' => array('2.1','3.0'),	'limit' => true ),
+            'FN'		=> array( 'req'	=> array('3.0') ),
+            'N'			=> array( 'req'	=> array('2.1','3.0') ),
+            'PROFILE'	=> array( 'vers'=> array('3.0'),		'limit' => true ),
+            'NAME'		=> array( 'vers'=> array('3.0'),		'limit' => true ),
+            'SOURCE'	=> array( 'vers'=> array('3.0'),		'limit' => true ),
+            'NICKNAME'	=> array( 'vers'=> array('3.0') ),
+            'PHOTO' 	=> array( ),
+            'BDAY'		=> array( ),
+            'ADR'		=> array( ),	// 'limit' => false
+            'LABEL'		=> array( ),	// 'limit' => false
+            'TEL'		=> array( ),	// 'limit' => false
+            'EMAIL'		=> array( ),	// 'limit' => false
+            'MAILER'	=> array( ),
+            'TZ'		=> array( ),
+            'GEO'		=> array( ),
+            'TITLE'		=> array( ),
+            'ROLE'		=> array( ),
+            'LOGO'		=> array( ),
+            'AGENT'		=> array( ),
+            'ORG'		=> array( ),
+            'CATEGORIES'=> array( 'vers' => array('3.0') ),
+            'NOTE'		=> array( ),
+            'PRODID'	=> array( 'vers' => array('3.0') ),
+            'REV'		=> array( ),
+            'SORT-STRING'=>array( 'vers' => array('3.0') ),
+            'SOUND'		=> array( ),
+            'UID'		=> array( ),
+            'URL'		=> array( ),
+            'CLASS'		=> array( 'vers' => array('3.0') ),
+            'KEY'		=> array( ),
+        );
+        $ver = $this->getValue('VERSION');
+        $newline = $ver == '2.1'
+            ? "\r\n"	// version 2.1 uses \r\n for new lines
+            : "\n";		// version 3.0 uses \n
+
+        // initialize the vCard lines
+        $lines = array();
+
+        $lines[] = 'BEGIN:VCARD';
+
+        $prop_keys = array_keys($this->value);
+
+        foreach ( $prop_dfns as $prop => $prop_dfn ) {
+            if ( !is_array($prop_dfn) ) {
+                $prop_dfn = array( 'func' => $prop_dfn );
+            }
+            $prop_dfn = array_merge($prop_dfn_default, $prop_dfn);
+            if ( false !== $key = array_search($prop, $prop_keys) ) {
+                unset($prop_keys[$key]);
+            }
+            $prop_exists = isset($this->value[$prop]) && is_array($this->value[$prop]);
+            if ( $prop == 'PROFILE' && in_array($ver, $prop_dfn['vers']) ) {
+                // special case... don't really care what current val is
+                $lines[] = 'PROFILE:VCARD';
+            } elseif ( $prop_exists ) {
+                if ( in_array($ver, $prop_dfn['vers']) ) {
+                    foreach ( $this->value[$prop] as $iter => $val ) {
+                        $lines[] = $this->get($prop, $iter);
+                        if ( $prop_dfn['limit'] ) {
+                            break;
+                        }
+                    }
+                }
+            } elseif ( in_array($ver, $prop_dfn['req']) ) {
+                throw new File_IMC_Exception(
+                    $prop.' not set (required).',
+                    FILE_IMC::ERROR_PARAM_NOT_SET
+                );
+            }
+        }
+        // now build the extension properties
+        foreach ( $prop_keys as $prop ) {
+            if ( strpos($prop, 'X-') === 0 ) {
+                foreach ($this->value[$prop] as $key => $val) {
+                    $lines[] = $this->get($prop, $key);
+                }
+            }
+        }
+
+        $lines[] = 'END:VCARD';
+
+        // fold lines at 75 characters
+        $regex = '/(.{1,75})/i';
+        foreach ( $lines as $key => $val ) {
+            if (strlen($val) > 75) {
+                // we trim to drop the last newline, which will be added
+                // again by the implode function at the end of fetch()
+                $lines[$key] = trim(preg_replace($regex, "\\1$newline ", $val));
+            }
+        }
+
+        // compile the array of lines into a single text block and return
+        return implode($newline, $lines);
+    }
+
+    /********* deprecated methods *********/
+
+    /**
+    * addAddress
+    *
+    * @param string $pob      p.o. box
+    * @param string $extend   "extended address"
+    * @param string $street   street address
+    * @param string $locality locailty (e.g., city)
+    * @param string $region   region (e.g., state, province, or governorate)
+    * @param string $postcode postal code (e.g., ZIP code)
+    * @param string $country  country-name
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addAddress()
+    {
+        $args = func_get_args();
+        return $this->set('ADR', $args, 'new');
+    }
+
+    /**
+    * addCategories
+    *
+    * @param string $val Categories
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addCategories($val)
+    {
+        return $this->set('CATEGORIES', $val, 'new');
+    }
+
+    /**
+    * addEmail
+    *
+    * @param string $val email
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addEmail($val)
+    {
+        return $this->set('EMAIL', $val, 'new');
+    }
+
+    /**
+    * addLabel
+    *
+    * @param string $val label
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addLabel($val)
+    {
+        return $this->set('LABEL', $val, 'new');
+    }
+
+    /**
+    * addNickname
+    *
+    * @param string $val nickname
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addNickname($val)
+    {
+        return $this->set('NICKNAME', $val, 'new');
+    }
+
+    /**
+    * addOrganization
+    *
+    * @param string $val    organization
+    * @param bool   $append append or replace
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addOrganization($val,$append=true)
+    {
+        if ( $append && !empty($this->value['ORG'][0]) ) {
+            settype($val, 'array');
+            $vals_cur = array();
+            foreach ( $this->value['ORG'][0] as $part_num => $part_val ) {
+                $vals_cur = array_merge($vals_cur, $part_val);
+            }
+            $val = array_merge($vals_cur, $val);
+        }
+        return $this->set('ORG', $val);
+    }
+
+    /**
+    * addTelephone
+    *
+    * @param string $val telephone
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function addTelephone($val)
+    {
+        return $this->set('TEL', $val, 'new');
+    }
+
+    /**
+    * getAddress
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getAddress($iter=0)
+    {
+        return $this->get('ADR', $iter);
+    }
+
+    /**
+    * getAgent
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getAgent($iter=0)
+    {
+        return $this->get('AGENT', $iter);
+    }
+
+    /**
+    * getBirthday
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getBirthday($iter=0)
+    {
+        return $this->get('BDAY', $iter);
+    }
+
+    /**
+    * getCategories
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getCategories($iter=0)
+    {
+        return $this->get('CATEGORIES', $iter);
+    }
+
+    /**
+    * getClass
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getClass($iter=0)
+    {
+        return $this->get('CLASS', $iter);
+    }
+
+    /**
+    * getEmail
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getEmail($iter=0)
+    {
+        return $this->get('EMAIL', $iter);
+    }
+
+    /**
+    * getFormattedName
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getFormattedName($iter=0)
+    {
+        return $this->get('FN', $iter);
+    }
+
+    /**
+    * getGeo
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getGeo($iter=0)
+    {
+        return $this->get('GEO', $iter);
+    }
+
+    /**
+    * getKey
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getKey($iter=0)
+    {
+        return $this->get('KEY', $iter);
+    }
+
+    /**
+    * getLabel
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getLabel($iter=0)
+    {
+        return $this->get('LABEL', $iter);
+    }
+
+    /**
+    * getLogo
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getLogo($iter=0)
+    {
+        return $this->get('LOGO', $iter);
+    }
+
+    /**
+    * getMailer
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getMailer($iter=0)
+    {
+        return $this->get('MAILER', $iter);
+    }
+
+    /**
+    * getName
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getName($iter=0)
+    {
+        return $this->get('N', $iter);
+    }
+
+    /**
+    * getNickname
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getNickname($iter=0)
+    {
+        return $this->get('NICKNAME', $iter);
+    }
+
+    /**
+    * getNote
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getNote($iter=0)
+    {
+        return $this->get('NOTE', $iter);
+    }
+
+    /**
+    * getOrganization
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getOrganization($iter=0)
+    {
+        return $this->get('ORG', $iter);
+    }
+
+    /**
+    * getPhoto
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getPhoto($iter=0)
+    {
+        return $this->get('PHOTO', $iter);
+    }
+
+    /**
+    * getProductID
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getProductID($iter=0)
+    {
+        return $this->get('PRODID', $iter);
+    }
+
+    /**
+    * getRevision
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getRevision($iter=0)
+    {
+        return $this->get('REV', $iter);
+    }
+
+    /**
+    * getRole
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getRole($iter=0)
+    {
+        return $this->get('ROLE', $iter);
+    }
+
+    /**
+    * getSortString
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getSortString($iter=0)
+    {
+        return $this->get('SORT-STRING', $iter);
+    }
+
+    /**
+    * getSound
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getSound($iter=0)
+    {
+        return $this->get('SOUND', $iter);
+    }
+
+    /**
+    * getSource
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getSource($iter=0)
+    {
+        return $this->get('SOURCE', $iter);
+    }
+
+    /**
+    * getSourceName
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getSourceName($iter=0)
+    {
+        return $this->get('NAME', $iter);
+    }
+
+    /**
+    * getTZ
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getTZ($iter=0)
+    {
+        return $this->get('TZ', $iter);
+    }
+
+    /**
+    * getTelephone
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getTelephone($iter=0)
+    {
+        return $this->get('TEL', $iter);
+    }
+
+    /**
+    * getTitle
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getTitle($iter=0)
+    {
+        return $this->get('TITLE', $iter);
+    }
+
+    /**
+    * getURL
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getURL($iter=0)
+    {
+        return $this->get('URL', $iter);
+    }
+
+    /**
+    * getUniqueID
+    *
+    * @param int $iter iteration
+    *
+    * @return string
+    * @deprecated
+    * @see self::get()
+    */
+    public function getUniqueID($iter=0)
+    {
+        return $this->get('UID', $iter);
+    }
+
+    /**
+    * setAgent
+    *
+    * @param string $val agent
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setAgent($val)
+    {
+        return $this->set('AGENT', $val);
+    }
+
+    /**
+    * setBirthday
+    *
+    * @param string $val birthday
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setBirthday($val)
+    {
+        return $this->set('BDAY', $val);
+    }
+
+    /**
+    * setClass
+    *
+    * @param string $val class
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setClass($val)
+    {
+        return $this->set('CLASS', $val);
+    }
+
+    /**
+    * setFormattedName
+    *
+    * @param string $val formattedName
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setFormattedName($val)
+    {
+        return $this->set('FN', $val);
+    }
+
+    /**
+    * setGeo
+    *
+    * @param string $lat latitude
+    * @param string $lon longitude
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setGeo()
+    {
+        $args = func_get_args();
+        return $this->set('GEO', $args);
+    }
+
+    /**
+    * setKey
+    *
+    * @param string $val key
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setKey($val)
+    {
+        return $this->set('KEY', $val);
+    }
+
+    /**
+    * setLogo
+    *
+    * @param string $val logo
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setLogo($val)
+    {
+        return $this->set('LOGO', $val);
+    }
+
+    /**
+    * setMailer
+    *
+    * @param string $val mailer
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setMailer($val)
+    {
+        return $this->set('MAILER', $val);
+    }
+
+    /**
+    * setName
+    *
+    * @param string $family family/last name.
+    * @param string $given  given/first name.
+    * @param string $addl   additional/middle name.
+    * @param string $prefix honorific prefix such as Mr., Miss, etc.
+    * @param string $suffix honorific suffix such as III, Jr., Ph.D., etc.
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setName()
+    {
+        $args = func_get_args();
+        return $this->set('N', $args);
+    }
+
+    /**
+    * setNote
+    *
+    * @param string $val note
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setNote($val)
+    {
+        return $this->set('NOTE', $val);
+    }
+
+    /**
+    * setPhoto
+    *
+    * @param string $val photo
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setPhoto($val)
+    {
+        return $this->set('PHOTO', $val);
+    }
+
+    /**
+    * setProductID
+    *
+    * @param string $val productID
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setProductID($val)
+    {
+        return $this->set('PRODID', $val);
+    }
+
+    /**
+    * setRevision
+    *
+    * @param string $val revision
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setRevision($val)
+    {
+        return $this->set('REV', $val);
+    }
+
+    /**
+    * setRole
+    *
+    * @param string $val role
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setRole($val)
+    {
+        return $this->set('ROLE', $val);
+    }
+
+    /**
+    * setSortString
+    *
+    * @param string $val sortString
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setSortString($val)
+    {
+        return $this->set('SORT-STRING', $val);
+    }
+
+    /**
+    * setSound
+    *
+    * @param string $val sound
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setSound($val)
+    {
+        return $this->set('SOUND', $val);
+    }
+
+    /**
+    * setSource
+    *
+    * @param string $val source
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setSource($val)
+    {
+        return $this->set('SOURCE', $val);
+    }
+
+    /**
+    * setSourceName
+    *
+    * @param string $val sourceName
+    *
+    * @return $this
+    *
+    * @deprecated
+    * @see self::set()
+    */
+    public function setSourceName($val)
+    {
+        return $this->set('NAME', $val);
+    }
+
+    /**
+    * setTZ
+    *
+    * @param string $val TZ
+    *
+    * @return $this
+    *
+    * @deprecated
+    * @see self::set()
+    */
+    public function setTZ($val)
+    {
+        return $this->set('TZ', $val);
+    }
+
+    /**
+    * setTitle
+    *
+    * @param string $val title
+    *
+    * @return $this
+    *
+    * @deprecated
+    * @see self::set()
+    */
+    public function setTitle($val)
+    {
+        return $this->set('TITLE', $val);
+    }
+
+    /**
+    * setURL
+    *
+    * @param string $val URL
+    *
+    * @return $this
+    *
+    * @deprecated
+    * @see self::set()
+    */
+    public function setURL($val)
+    {
+        return $this->set('URL', $val);
+    }
+
+    /**
+    * setUniqueID
+    *
+    * @param string $val uniqueID
+    *
+    * @return $this
+    * @deprecated
+    * @see self::set()
+    */
+    public function setUniqueID($val)
+    {
+        return $this->set('UID', $val);
+    }
 
 }
 
-?>
\ No newline at end of file
+?>

From 96e25ac1e266a0dcfa7b078c8ea7ade81e5935c9 Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Wed, 31 Aug 2016 23:13:02 -0500
Subject: [PATCH 16/17] tests

---
 tests/AllTests.php | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/tests/AllTests.php b/tests/AllTests.php
index f6f4672..797fb9f 100644
--- a/tests/AllTests.php
+++ b/tests/AllTests.php
@@ -25,14 +25,21 @@
 if ($svnOrNot == '@package_version@') {
     // we run from svn and fiddle with the include_path
     set_include_path(
-        realpath($root)
+        get_include_path()
         . PATH_SEPARATOR
-        . get_include_path()
+        . realpath($root)
     );
 }
 
 if (file_exists($root . '/vendor/autoload.php')) {
     require $root . '/vendor/autoload.php';
+} else {
+    /*
+     * Files needed by PhpUnit
+     */
+    require_once 'PHPUnit/Autoload.php';
+    require_once 'PHPUnit/TextUI/TestRunner.php';
+    require_once 'PHPUnit/Extensions/PhptTestSuite.php';
 }
 
 /**
@@ -45,13 +52,6 @@
 }
 
 
-/*
- * Files needed by PhpUnit
- */
-require_once 'PHPUnit/Autoload.php';
-require_once 'PHPUnit/TextUI/TestRunner.php';
-require_once 'PHPUnit/Extensions/PhptTestSuite.php';
-
 /*
  * You must add each additional class-level test suite file here
  */

From 009965062cee7745742751c19f3b04ffc6d7ff5e Mon Sep 17 00:00:00 2001
From: Brad Kent 
Date: Thu, 1 Sep 2016 09:22:21 -0500
Subject: [PATCH 17/17] PHPUnit updates + .gitattributes

phpunit updates & whitespace

added .gitattributes: "* text eol=lf"
test was failing due to line-ending conversion
---
 .gitattributes                         |    2 +
 File/IMC/Build/Vcard.php               | 1644 ++++++++++++------------
 tests/File/IMC/BugsTest.php            |    6 -
 tests/File/IMC/BuildTest.php           |    6 -
 tests/File/IMC/Parse/VcalendarTest.php |    6 -
 tests/File/IMC/ParseTest.php           |    6 -
 6 files changed, 820 insertions(+), 850 deletions(-)
 create mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..e7c1d93
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text eol=lf
diff --git a/File/IMC/Build/Vcard.php b/File/IMC/Build/Vcard.php
index 41f9967..96479d4 100644
--- a/File/IMC/Build/Vcard.php
+++ b/File/IMC/Build/Vcard.php
@@ -43,68 +43,65 @@
  */
 
 /**
-* This class builds a single vCard (version 3.0 or 2.1).
-*
-* @category File_Formats
-* @package  File_IMC
-* @author   Paul M. Jones 
-* @license  http://www.opensource.org/licenses/bsd-license.php The BSD License
-* @version  Release: @package_version@
-* @link     http://pear.php.net/package/File_IMC
-*/
+ * This class builds a single vCard (version 3.0 or 2.1).
+ *
+ * @category File_Formats
+ * @package  File_IMC
+ * @author   Paul M. Jones 
+ * @license  http://www.opensource.org/licenses/bsd-license.php The BSD License
+ * @version  Release: @package_version@
+ * @link     http://pear.php.net/package/File_IMC
+ */
 class File_IMC_Build_Vcard extends File_IMC_Build
 {
     /**
-    * Constructor
-    *
-    * @param string $version The vCard version to build; affects which
-    * parameters are allowed and which properties are returned by
-    * fetch().
-    *
-    * @return File_IMC_Build_Vcard
-    *
-    * @see  parent::fetch()
-    * @uses parent::reset()
-    */
+     * Constructor
+     *
+     * @param string $version The vCard version to build; affects which
+     * parameters are allowed and which properties are returned by
+     * fetch().
+     *
+     * @return File_IMC_Build_Vcard
+     *
+     * @see  parent::fetch()
+     * @uses parent::reset()
+     */
     public function __construct($version = '3.0')
     {
         $this->reset($version);
     }
 
     /**
-    * setVersion
-    *
-    * @param string $val version
-    *
-    * @return null
-    */
-    public function setVersion($val='3.0')
+     * setVersion
+     *
+     * @param string $val version
+     *
+     * @return null
+     */
+    public function setVersion($val = '3.0')
     {
         $this->set('VERSION', $val);
     }
 
     /**
-    * Validates parameter names and values based on the vCard version
-    * (2.1 or 3.0).
-    *
-    * @param string $name The parameter name (e.g., TYPE or ENCODING).
-    *
-    * @param string $text The parameter value (e.g., HOME or BASE64).
-    *
-    * @param string $prop Optional, the propety name (e.g., ADR or PHOTO).
-    *						Only used for error messaging.
-    *
-    * @param int    $iter Optional, the iteration of the property.
-    *						Only used for error messaging.
-    *
-    * @return mixed	Boolean true if the parameter is valid
-    * @access public
-    * @throws File_IMC_Exception ... if not.
-    *
-    * @uses self::validateParam21()
-    * @uses self::validateParam30()
-    */
-    public function validateParam($name, $text, $prop=null, $iter=null)
+     * Validates parameter names and values based on the vCard version
+     * (2.1 or 3.0).
+     *
+     * @param string $name The parameter name (e.g., TYPE or ENCODING).
+     * @param string $text The parameter value (e.g., HOME or BASE64).
+     * @param string $prop Optional, the propety name (e.g., ADR or PHOTO).
+     *						Only used for error messaging.
+     * @param int    $iter Optional, the iteration of the property.
+     *						Only used for error messaging.
+     *
+     * @return mixed	Boolean true if the parameter is valid
+     * @access public
+     * @throws File_IMC_Exception ... if not.
+     *
+     * @uses self::validateParam21()
+     * @uses self::validateParam30()
+     */
+    public function validateParam($name, $text, $prop = null, $iter = null)
     {
         $name = strtoupper($name);
         $text = strtoupper($text);
@@ -117,9 +114,9 @@ public function validateParam($name, $text, $prop=null, $iter=null)
                 FILE_IMC::ERROR_INVALID_PARAM
             );
         }
-        if ( $this->value['VERSION'][0][0][0] == '2.1' ) {
+        if ($this->value['VERSION'][0][0][0] == '2.1') {
             return $this->_validateParam21($name, $text, $prop, $iter);
-        } elseif ( $this->value['VERSION'][0][0][0] == '3.0' ) {
+        } elseif ($this->value['VERSION'][0][0][0] == '3.0') {
             return $this->_validateParam30($name, $text, $prop, $iter);
         }
         throw new File_IMC_Exception(
@@ -146,48 +143,48 @@ protected function _validateParam21($name, $text, $prop, $iter)
         // Validate against version 2.1 (pretty strict)
         $x_val = strpos($text, 'X-') === 0;
         switch ($name) {
-        case 'TYPE':
-            static $types = array (
-                // ADR
-                'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
-                // TEL
-                'PREF','VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS',
-                'MODEM', 'CAR', 'ISDN', 'VIDEO',
-                //EMAIL
-                'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD','INTERNET',
-                    'IBMMAIL', 'MCIMAIL','POWERSHARE', 'PRODIGY', 'TLX', 'X400',
-                //PHOTO & LOGO
-                'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB', 'PICT', 'TIFF',
-                    'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME',
-                //SOUND
-                'WAVE', 'AIFF', 'PCM',
-                // KEY
-                'X509', 'PGP'
-            );
-            $result = ( in_array($text, $types) || $x_val );
-            break;
-        case 'ENCODING':
-            $vals = array('7BIT','8BIT','BASE64','QUOTED-PRINTABLE');
-            $result = ( in_array($text, $vals) || $x_val );
-            break;
-        case 'CHARSET':  // all charsets are OK
-        case 'LANGUAGE': // all languages are OK
-            $result = true;
-            break;
-        case 'VALUE':
-            $vals = array('INLINE','CONTENT-ID','CID','URL','VCARD');
-            $result = ( in_array($text, $vals) || $x_val );
-            break;
-        default:
-            $result = ( strpos($name, 'X-') === 0 );
-            /*
-            if ( !$result )
-                throw new File_IMC_Exception(
-                    'vCard 2.1 ['.$prop.']['.$iter.']: '
-                    .'"'.$name.'" is an unknown or invalid parameter name.',
-                    FILE_IMC::ERROR_INVALID_PARAM);
-            */
-            break;
+            case 'TYPE':
+                static $types = array (
+                    // ADR
+                    'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
+                    // TEL
+                    'PREF','VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', 'BBS',
+                    'MODEM', 'CAR', 'ISDN', 'VIDEO',
+                    // EMAIL
+                    'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD','INTERNET',
+                        'IBMMAIL', 'MCIMAIL','POWERSHARE', 'PRODIGY', 'TLX', 'X400',
+                    // PHOTO & LOGO
+                    'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB', 'PICT', 'TIFF',
+                        'PDF', 'PS', 'JPEG', 'MPEG', 'MPEG2', 'AVI', 'QTIME',
+                    // SOUND
+                    'WAVE', 'AIFF', 'PCM',
+                    // KEY
+                    'X509', 'PGP'
+                );
+                $result = ( in_array($text, $types) || $x_val );
+                break;
+            case 'ENCODING':
+                $vals = array('7BIT','8BIT','BASE64','QUOTED-PRINTABLE');
+                $result = ( in_array($text, $vals) || $x_val );
+                break;
+            case 'CHARSET':  // all charsets are OK
+            case 'LANGUAGE': // all languages are OK
+                $result = true;
+                break;
+            case 'VALUE':
+                $vals = array('INLINE','CONTENT-ID','CID','URL','VCARD');
+                $result = ( in_array($text, $vals) || $x_val );
+                break;
+            default:
+                $result = ( strpos($name, 'X-') === 0 );
+                /*
+                if ( !$result )
+                    throw new File_IMC_Exception(
+                        'vCard 2.1 ['.$prop.']['.$iter.']: '
+                        .'"'.$name.'" is an unknown or invalid parameter name.',
+                        FILE_IMC::ERROR_INVALID_PARAM);
+                */
+                break;
         }
         /*
         if ( !$result )
@@ -217,28 +214,28 @@ protected function _validateParam30($name, $text, $prop, $iter)
         // Validate against version 3.0 (pretty lenient)
         $x_val = strpos($text, 'X-') === 0;
         switch ($name) {
-        case 'TYPE':     // all types are OK
-        case 'LANGUAGE': // all languages are OK
-            $result = true;
-            break;
-        case 'ENCODING':
-            $vals = array('8BIT','B');
-            $result = ( in_array($text, $vals) || $x_val );
-            break;
-        case 'VALUE':
-            $vals = array('BINARY','PHONE-NUMBER','TEXT','URI','UTC-OFFSET','VCARD');
-            $result = ( in_array($text, $vals) || $x_val );
-            break;
-        default:
-            $result = ( strpos($name, 'X-') === 0 );
-            /*
-            if ( !$result )
-                throw new File_IMC_Exception(
-                    'vCard 3.0 ['.$prop.']['.$iter.']: '
-                    .'"'.$name.'" is an unknown or invalid parameter name.',
-                    FILE_IMC::ERROR_INVALID_PARAM);
-            */
-            break;
+            case 'TYPE':     // all types are OK
+            case 'LANGUAGE': // all languages are OK
+                $result = true;
+                break;
+            case 'ENCODING':
+                $vals = array('8BIT','B');
+                $result = ( in_array($text, $vals) || $x_val );
+                break;
+            case 'VALUE':
+                $vals = array('BINARY','PHONE-NUMBER','TEXT','URI','UTC-OFFSET','VCARD');
+                $result = ( in_array($text, $vals) || $x_val );
+                break;
+            default:
+                $result = ( strpos($name, 'X-') === 0 );
+                /*
+                if ( !$result )
+                    throw new File_IMC_Exception(
+                        'vCard 3.0 ['.$prop.']['.$iter.']: '
+                        .'"'.$name.'" is an unknown or invalid parameter name.',
+                        FILE_IMC::ERROR_INVALID_PARAM);
+                */
+                break;
         }
         /*
         if ( !$result )
@@ -251,24 +248,24 @@ protected function _validateParam30($name, $text, $prop, $iter)
     }
 
     /**
-    * Sets the value of one entire ADR iteration.
-    *
-    * @param array $value address components
-    *   post-office-box
-    *   extended-address
-    *   street-address
-    *   locality		: (e.g., city)
-    *   region			: (e.g., state, province, or governorate)
-    *   postal-code		: (e.g., ZIP code)
-    *	country-name
-    *  value may be passed as a numeric or key/value array
-    *  (keys coming from hCard microformat specification)
-    *  each component may be a String (one repetition) or array (multiple reptitions)
-    * @param int   $iter  iteration
-    *
-    * @return $this
-    * @access private
-    */
+     * Sets the value of one entire ADR iteration.
+     *
+     * @param array $value address components
+     *   post-office-box
+     *   extended-address
+     *   street-address
+     *   locality		: (e.g., city)
+     *   region			: (e.g., state, province, or governorate)
+     *   postal-code		: (e.g., ZIP code)
+     *	country-name
+     *  value may be passed as a numeric or key/value array
+     *  (keys coming from hCard microformat specification)
+     *  each component may be a String (one repetition) or array (multiple reptitions)
+     * @param int   $iter  iteration
+     *
+     * @return void
+     * @access private
+     */
     protected function _setADR($value, $iter)
     {
         $keys = array(
@@ -280,11 +277,11 @@ protected function _setADR($value, $iter)
             'postal-code',
             'country-name',
         );
-        foreach ( $keys as $i => $k ) {
-            if ( isset($value[$k]) ) {
+        foreach ($keys as $i => $k) {
+            if (isset($value[$k])) {
                 $value[$i] = $value[$k];
             }
-            if ( !isset($value[$i]) ) {
+            if (!isset($value[$i])) {
                 $value[$i] = '';
             }
         }
@@ -298,23 +295,23 @@ protected function _setADR($value, $iter)
     }
 
     /**
-    * Sets the FN property of the card.  If no text is passed as the
-    * FN value, constructs an FN automatically from N property.
-    *
-    * @param string $text Override the automatic generation of FN from N
-    *    elements with the specified text.
-    * @param int    $iter Iteration
-    *
-    * @return mixed Void on success
-    * @access private
-    * @throws File_IMC_Exception ... on failure.
-    */
-    protected function _setFN($text=null, $iter=0)
+     * Sets the FN property of the card.  If no text is passed as the
+     * FN value, constructs an FN automatically from N property.
+     *
+     * @param string $text Override the automatic generation of FN from N
+     *    elements with the specified text.
+     * @param int    $iter Iteration
+     *
+     * @return         void
+     * @access private
+     * @throws         File_IMC_Exception ... on failure.
+     */
+    protected function _setFN($text = null, $iter = 0)
     {
-        if ( $text === null ) {
+        if ($text === null) {
             // no text was specified for the FN, so build it
             // from the current N property if an N exists
-            if ( is_array($this->value['N']) ) {
+            if (is_array($this->value['N'])) {
                 // build from N.
                 // first (given) name, first iteration, first repetition
                 $text .= $this->getValue('N', 0, FILE_IMC::VCARD_N_GIVEN, 0);
@@ -342,27 +339,27 @@ protected function _setFN($text=null, $iter=0)
     }
 
     /**
-    * Sets the GEO property (both latitude and longitude)
-    *
-    * @param array $value coords lat and lon
-    *     value may be passed as a numeric or key/value array
-    *     (keys coming from geo microformat specification)
-    * @param int   $iter  iteration
-    *
-    * @return $this
-    * @access private
-    */
+     * Sets the GEO property (both latitude and longitude)
+     *
+     * @param array $value coords lat and lon
+     *     value may be passed as a numeric or key/value array
+     *     (keys coming from geo microformat specification)
+     * @param int   $iter  iteration
+     *
+     * @return void
+     * @access private
+     */
     protected function _setGEO($value, $iter)
     {
         $keys = array(
             'latitude',
             'longitude',
         );
-        foreach ( $keys as $i => $k ) {
-            if ( isset($value[$k]) ) {
+        foreach ($keys as $i => $k) {
+            if (isset($value[$k])) {
                 $value[$i] = $value[$k];
             }
-            if ( !isset($value[$i]) ) {
+            if (!isset($value[$i])) {
                 $value[$i] = '';
             }
         }
@@ -371,23 +368,23 @@ protected function _setGEO($value, $iter)
     }
 
     /**
-    * Sets the full N property of the vCard.
-    *
-    * @param array $value name comonents
-    *	family-name		: family/last name.
-    *	given-name		: given/first name.
-    *	additional-name	: additional/middle name.
-    *	honorific-prefix: prefix such as Mr., Miss, etc.
-    *	honorific-suffix: suffix such as III, Jr., Ph.D., etc.
-    * value may be passed as a numeric or key/value array
-    *   (keys coming from hCard microformat specification)
-    * each component may be a string or array
-    * @param int   $iter  iteration
-    *
-    * @return null
-    * @access private
-    */
-    protected function _setN($value,$iter)
+     * Sets the full N property of the vCard.
+     *
+     * @param array $value name comonents
+     *	family-name		: family/last name.
+     *	given-name		: given/first name.
+     *	additional-name	: additional/middle name.
+     *	honorific-prefix: prefix such as Mr., Miss, etc.
+     *	honorific-suffix: suffix such as III, Jr., Ph.D., etc.
+     * value may be passed as a numeric or key/value array
+     *   (keys coming from hCard microformat specification)
+     * each component may be a string or array
+     * @param int   $iter  iteration
+     *
+     * @return void
+     * @access private
+     */
+    protected function _setN($value, $iter)
     {
         $keys = array(
             'family-name',
@@ -396,11 +393,11 @@ protected function _setN($value,$iter)
             'honorific-prefix',
             'honorific-suffix',
         );
-        foreach ( $keys as $i => $k ) {
-            if ( isset($value[$k]) ) {
+        foreach ($keys as $i => $k) {
+            if (isset($value[$k])) {
                 $value[$i] = $value[$k];
             }
-            if ( !isset($value[$i]) ) {
+            if (!isset($value[$i])) {
                 $value[$i] = '';
             }
         }
@@ -412,32 +409,32 @@ protected function _setN($value,$iter)
     }
 
     /**
-    * Sets the full value of the ORG property.
-    *
-    * The ORG property can have one or more parts (as opposed to
-    * repetitions of values within those parts).  The first part is the
-    * highest-level organization, the second part is the next-highest,
-    * the third part is the third-highest, and so on.  There can by any
-    * number of parts in one ORG iteration.  (This is different from
-    * other properties, such as NICKNAME, where an iteration has only
-    * one part but may have many repetitions within that part.)
-    *
-    * @param mixed $value String (one ORG part) or array (of ORG parts)
-    *     to use as the value for the property iteration.
-    * @param int   $iter  iteration
-    *
-    * @return null
-    * @access private
-    */
-    protected function _setORG($value,$iter)
+     * Sets the full value of the ORG property.
+     *
+     * The ORG property can have one or more parts (as opposed to
+     * repetitions of values within those parts).  The first part is the
+     * highest-level organization, the second part is the next-highest,
+     * the third part is the third-highest, and so on.  There can by any
+     * number of parts in one ORG iteration.  (This is different from
+     * other properties, such as NICKNAME, where an iteration has only
+     * one part but may have many repetitions within that part.)
+     *
+     * @param mixed $value String (one ORG part) or array (of ORG parts)
+     *     to use as the value for the property iteration.
+     * @param int   $iter  iteration
+     *
+     * @return void
+     * @access private
+     */
+    protected function _setORG($value, $iter)
     {
         $keys = array(
             'organization-name',
             'organization-unit',	// may pass an array
         );
         settype($value, 'array');
-        foreach ( $keys as $i => $k ) {
-            if ( isset($value[$k]) ) {
+        foreach ($keys as $k) {
+            if (isset($value[$k])) {
                 $value[] = $value[$k];
                 unset($value[$k]);
             }
@@ -445,19 +442,19 @@ protected function _setORG($value,$iter)
         // flatten the array
         $vals = $value;
         $value = array();
-        foreach ( $vals as $v ) {
+        foreach ($vals as $v) {
             settype($v, 'array');
             $value = array_merge($value, $v);
         }
         // clear existing value
-        if ( isset($this->value['ORG'][$iter]) ) {
+        if (isset($this->value['ORG'][$iter])) {
             unset($this->value['ORG'][$iter]);
         }
         // set the new value(s)
-        foreach ( $value as $k => $v) {
+        foreach ($value as $k => $v) {
             settype($v, 'array');
-            foreach ( $v as $v2 ) {
-                if ( !empty($v2) ) {
+            foreach ($v as $v2) {
+                if (!empty($v2)) {
                     $this->setValue('ORG', $iter, $k, $v2);
                 }
             }
@@ -465,14 +462,14 @@ protected function _setORG($value,$iter)
     }
 
     /**
-    * Gets back the value of one ADR property iteration.
-    *
-    * @param int $iter The property iteration-number to get the value for.
-    *
-    * @return mixed The value of this property iteration, or ...
-    * @access private
-    * @throws File_IMC_Exception ... if the iteration is not valid.
-    */
+     * Gets back the value of one ADR property iteration.
+     *
+     * @param int $iter The property iteration-number to get the value for.
+     *
+     * @return mixed The value of this property iteration, or ...
+     * @access private
+     * @throws File_IMC_Exception ... if the iteration is not valid.
+     */
     protected function _getADR($iter)
     {
         if (! is_integer($iter) || $iter < 0) {
@@ -492,13 +489,13 @@ protected function _getADR($iter)
     }
 
     /**
-    * Gets back the value of the GEO property.
-    *
-    * @param int $iter The property iteration-number to get
-    *
-    * @return string The value of this property.
-    * @access private
-    */
+     * Gets back the value of the GEO property.
+     *
+     * @param int $iter The property iteration-number to get
+     *
+     * @return string The value of this property.
+     * @access private
+     */
     protected function _getGEO($iter)
     {
         return $this->getMeta('GEO', $iter)
@@ -507,13 +504,13 @@ protected function _getGEO($iter)
     }
 
     /**
-    * Gets back the full N property
-    *
-    * @param int $iter The property iteration-number to get the value for.
-    *
-    * @return string
-    * @access private
-    */
+     * Gets back the full N property
+     *
+     * @param int $iter The property iteration-number to get the value for.
+     *
+     * @return string
+     * @access private
+     */
     protected function _getN($iter)
     {
         return $this->getMeta('N', $iter)
@@ -525,21 +522,21 @@ protected function _getN($iter)
     }
 
     /**
-    * Gets back the value of the ORG property.
-    *
-    * @param int $iter The property iteration-number to get the value for.
-    *
-    * @return string The value of this property.
-    * @access private
-    */
+     * Gets back the value of the ORG property.
+     *
+     * @param int $iter The property iteration-number to get the value for.
+     *
+     * @return string The value of this property.
+     * @access private
+     */
     protected function _getORG($iter)
     {
         $text	= $this->getMeta('ORG', $iter);
         $parts	= count($this->value['ORG'][$iter]);
         $last = $parts - 1;
-        for ( $part = 0; $part < $parts; $part++ ) {
+        for ($part = 0; $part < $parts; $part++) {
             $text .= $this->getValue('ORG', $iter, $part);
-            if ( $part != $last ) {
+            if ($part != $last) {
                 $text .= ';';
             }
         }
@@ -547,62 +544,62 @@ protected function _getORG($iter)
     }
 
     /**
-    * Sets the value of the specified property
-    *   for PHOTO, LOGO, SOUND, & KEY properties:
-    *		if a filepath is passed:, automatically base64-encodes
-    *			and sets ENCODING parameter
-    *		if a URL is passed, automatically sets the VALUE=URL|URI parameter
-    *
-    * _setPROPERTY($value,$iter) method will be used if exists  ( ie _setADR() )
-    *
-    * @param string $prop  property
-    * @param mixed  $value value
-    *       when property is ADR, GEO, or N:  value is an array
-    *			additionaly, the array may be an associateive array
-    *			ADR: 	post-office-box, extended-address, street-address,
-    *                      locality, region, postal-code, country-name
-    *			GEO:	latitude, longitude
-    *			N:		family-name, given-name, additional-name,
-    *                      honorific-prefix, honorific-suffix
-    *       when property is ORG, value may be an string or array
-    *			ORG		'organization-name','organization-unit'
-    *       for all other properties, value is a string
-    * @param mixed  $iter  iteration default = 0; pass 'new' to add an iteration
-    *
-    * @return $this
-    * @access public
+     * Sets the value of the specified property
+     *   for PHOTO, LOGO, SOUND, & KEY properties:
+     *		if a filepath is passed:, automatically base64-encodes
+     *			and sets ENCODING parameter
+     *		if a URL is passed, automatically sets the VALUE=URL|URI parameter
+     *
+     * _setPROPERTY($value,$iter) method will be used if exists  ( ie _setADR() )
+     *
+     * @param string $prop  property
+     * @param mixed  $value value
+     *       when property is ADR, GEO, or N:  value is an array
+     *			additionaly, the array may be an associateive array
+     *			ADR: 	post-office-box, extended-address, street-address,
+     *                      locality, region, postal-code, country-name
+     *			GEO:	latitude, longitude
+     *			N:		family-name, given-name, additional-name,
+     *                      honorific-prefix, honorific-suffix
+     *       when property is ORG, value may be an string or array
+     *			ORG		'organization-name','organization-unit'
+     *       for all other properties, value is a string
+     * @param mixed  $iter  iteration default = 0; pass 'new' to add an iteration
+     *
+     * @return $this
+     * @access public
     */
-    public function set($prop,$value,$iter=0)
+    public function set($prop, $value, $iter = 0)
     {
         $prop = strtoupper(trim($prop));
-        if ( $iter === 'new' ) {
+        if ($iter === 'new') {
             $iter = isset($this->value[$prop])
                 ? count($this->value[$prop])
                 : 0;
-        } elseif ( !is_integer($iter) || $iter < 0) {
+        } elseif (!is_integer($iter) || $iter < 0) {
             throw new File_IMC_Exception(
                 $prop.' iteration number not valid.',
                 FILE_IMC::ERROR_INVALID_ITERATION
             );
         }
         $method = '_set'.$prop;
-        if ( method_exists($this, $method) ) {
+        if (method_exists($this, $method)) {
             call_user_func(array($this,$method), $value, $iter);
         } else {
-            if ( $prop == 'VERSION' && !in_array($value, array('2.1','3.0')) ) {
+            if ($prop == 'VERSION' && !in_array($value, array('2.1','3.0'))) {
                 throw new File_IMC_Exception(
                     'Version must be 3.0 or 2.1 to be valid.',
                     FILE_IMC::ERROR_INVALID_VCARD_VERSION
                 );
-            } elseif ( in_array($prop, array('PHOTO','LOGO','SOUND','KEY')) ) {
-                if ( file_exists($value) ) {
+            } elseif (in_array($prop, array('PHOTO','LOGO','SOUND','KEY'))) {
+                if (file_exists($value)) {
                     $value = base64_encode(file_get_contents($value));
                 }
             }
             $this->setValue($prop, $iter, 0, $value);
-            if ( in_array($prop, array('PHOTO','LOGO','SOUND','KEY')) ) {
+            if (in_array($prop, array('PHOTO','LOGO','SOUND','KEY'))) {
                 $ver = $this->getValue('VERSION');
-                if ( preg_match('#^(https?|ftp)://#', $value) ) {
+                if (preg_match('#^(https?|ftp)://#', $value)) {
                     $this->addParam('VALUE', $ver == '2.1' ? 'URL' : 'URI');
                 } else {
                     $this->addParam('ENCODING', $ver == '2.1' ? 'BASE64' : 'B');
@@ -613,24 +610,24 @@ public function set($prop,$value,$iter=0)
     }
 
     /**
-    * Gets back the vcard line of the specified property
-    *    (property name, params, & value)
-    * This func removes the need for all the public getXxx functions...
-    *      uses the protected methods: _getADR, _getGEO, _getN, & _getORG
-    *
-    * _getPROPERTY($iter) method will be used if exists  ( ie _getADR() )
-    *
-    * @param string $prop property
-    * @param int    $iter iteration default = 0
-    *
-    * @return string The value of the property
-    * @access public
-    */
-    public function get($prop,$iter=0)
+     * Gets back the vcard line of the specified property
+     *    (property name, params, & value)
+     * This func removes the need for all the public getXxx functions...
+     *      uses the protected methods: _getADR, _getGEO, _getN, & _getORG
+     *
+     * _getPROPERTY($iter) method will be used if exists  ( ie _getADR() )
+     *
+     * @param string $prop property
+     * @param int    $iter iteration default = 0
+     *
+     * @return string The value of the property
+     * @access public
+     */
+    public function get($prop, $iter = 0)
     {
         $return = '';
         $prop = strtoupper(trim($prop));
-        if ( !is_integer($iter) || $iter < 0) {
+        if (!is_integer($iter) || $iter < 0) {
             throw new File_IMC_Exception(
                 $prop.' iteration number not valid.',
                 FILE_IMC::ERROR_INVALID_ITERATION
@@ -638,7 +635,7 @@ public function get($prop,$iter=0)
         }
         $this->encode($prop, $iter);
         $method = '_get'.$prop;
-        if ( method_exists($this, $method) ) {
+        if (method_exists($this, $method)) {
             $return = call_user_func(array($this,$method), $iter);
         } else {
             $return = $this->getMeta($prop, $iter).$this->getValue($prop, $iter, 0);
@@ -647,17 +644,17 @@ public function get($prop,$iter=0)
     }
 
     /**
-    * Fetches a full vCard text block based on $this->value and
-    * $this->param. The order of the returned properties is similar to
-    * their order in RFC 2426.  Honors the value of
-    * $this->value['VERSION'] to determine which vCard properties are
-    * returned (2.1- or 3.0-compliant).
-    *
-    * @return string A properly formatted vCard text block.
-    *
-    * @access public
-    * @uses self::get()
-    */
+     * Fetches a full vCard text block based on $this->value and
+     * $this->param. The order of the returned properties is similar to
+     * their order in RFC 2426.  Honors the value of
+     * $this->value['VERSION'] to determine which vCard properties are
+     * returned (2.1- or 3.0-compliant).
+     *
+     * @return string A properly formatted vCard text block.
+     *
+     * @access public
+     * @uses   self::get()
+     */
     public function fetch()
     {
         $prop_dfn_default = array(
@@ -710,28 +707,28 @@ public function fetch()
 
         $prop_keys = array_keys($this->value);
 
-        foreach ( $prop_dfns as $prop => $prop_dfn ) {
-            if ( !is_array($prop_dfn) ) {
+        foreach ($prop_dfns as $prop => $prop_dfn) {
+            if (!is_array($prop_dfn)) {
                 $prop_dfn = array( 'func' => $prop_dfn );
             }
             $prop_dfn = array_merge($prop_dfn_default, $prop_dfn);
-            if ( false !== $key = array_search($prop, $prop_keys) ) {
+            if (false !== $key = array_search($prop, $prop_keys)) {
                 unset($prop_keys[$key]);
             }
             $prop_exists = isset($this->value[$prop]) && is_array($this->value[$prop]);
-            if ( $prop == 'PROFILE' && in_array($ver, $prop_dfn['vers']) ) {
+            if ($prop == 'PROFILE' && in_array($ver, $prop_dfn['vers'])) {
                 // special case... don't really care what current val is
                 $lines[] = 'PROFILE:VCARD';
-            } elseif ( $prop_exists ) {
-                if ( in_array($ver, $prop_dfn['vers']) ) {
-                    foreach ( $this->value[$prop] as $iter => $val ) {
+            } elseif ($prop_exists) {
+                if (in_array($ver, $prop_dfn['vers'])) {
+                    foreach ($this->value[$prop] as $iter => $val) {
                         $lines[] = $this->get($prop, $iter);
-                        if ( $prop_dfn['limit'] ) {
+                        if ($prop_dfn['limit']) {
                             break;
                         }
                     }
                 }
-            } elseif ( in_array($ver, $prop_dfn['req']) ) {
+            } elseif (in_array($ver, $prop_dfn['req'])) {
                 throw new File_IMC_Exception(
                     $prop.' not set (required).',
                     FILE_IMC::ERROR_PARAM_NOT_SET
@@ -739,8 +736,8 @@ public function fetch()
             }
         }
         // now build the extension properties
-        foreach ( $prop_keys as $prop ) {
-            if ( strpos($prop, 'X-') === 0 ) {
+        foreach ($prop_keys as $prop) {
+            if (strpos($prop, 'X-') === 0) {
                 foreach ($this->value[$prop] as $key => $val) {
                     $lines[] = $this->get($prop, $key);
                 }
@@ -751,7 +748,7 @@ public function fetch()
 
         // fold lines at 75 characters
         $regex = '/(.{1,75})/i';
-        foreach ( $lines as $key => $val ) {
+        foreach ($lines as $key => $val) {
             if (strlen($val) > 75) {
                 // we trim to drop the last newline, which will be added
                 // again by the implode function at the end of fetch()
@@ -763,23 +760,25 @@ public function fetch()
         return implode($newline, $lines);
     }
 
-    /********* deprecated methods *********/
+    /*
+        ******** deprecated methods ********
+    */
 
     /**
-    * addAddress
-    *
-    * @param string $pob      p.o. box
-    * @param string $extend   "extended address"
-    * @param string $street   street address
-    * @param string $locality locailty (e.g., city)
-    * @param string $region   region (e.g., state, province, or governorate)
-    * @param string $postcode postal code (e.g., ZIP code)
-    * @param string $country  country-name
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addAddress
+     *
+     * @param string $pob      p.o. box
+     * @param string $extend   "extended address"
+     * @param string $street   street address
+     * @param string $locality locailty (e.g., city)
+     * @param string $region   region (e.g., state, province, or governorate)
+     * @param string $postcode postal code (e.g., ZIP code)
+     * @param string $country  country-name
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addAddress()
     {
         $args = func_get_args();
@@ -787,77 +786,77 @@ public function addAddress()
     }
 
     /**
-    * addCategories
-    *
-    * @param string $val Categories
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addCategories
+     *
+     * @param string $val Categories
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addCategories($val)
     {
         return $this->set('CATEGORIES', $val, 'new');
     }
 
     /**
-    * addEmail
-    *
-    * @param string $val email
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addEmail
+     *
+     * @param string $val email
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addEmail($val)
     {
         return $this->set('EMAIL', $val, 'new');
     }
 
     /**
-    * addLabel
-    *
-    * @param string $val label
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addLabel
+     *
+     * @param string $val label
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addLabel($val)
     {
         return $this->set('LABEL', $val, 'new');
     }
 
     /**
-    * addNickname
-    *
-    * @param string $val nickname
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addNickname
+     *
+     * @param string $val nickname
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addNickname($val)
     {
         return $this->set('NICKNAME', $val, 'new');
     }
 
     /**
-    * addOrganization
-    *
-    * @param string $val    organization
-    * @param bool   $append append or replace
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
-    public function addOrganization($val,$append=true)
+     * addOrganization
+     *
+     * @param string $val    organization
+     * @param bool   $append append or replace
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
+    public function addOrganization($val, $append = true)
     {
-        if ( $append && !empty($this->value['ORG'][0]) ) {
+        if ($append && !empty($this->value['ORG'][0])) {
             settype($val, 'array');
             $vals_cur = array();
-            foreach ( $this->value['ORG'][0] as $part_num => $part_val ) {
+            foreach ($this->value['ORG'][0] as $part_num => $part_val) {
                 $vals_cur = array_merge($vals_cur, $part_val);
             }
             $val = array_merge($vals_cur, $val);
@@ -866,491 +865,491 @@ public function addOrganization($val,$append=true)
     }
 
     /**
-    * addTelephone
-    *
-    * @param string $val telephone
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * addTelephone
+     *
+     * @param string $val telephone
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function addTelephone($val)
     {
         return $this->set('TEL', $val, 'new');
     }
 
     /**
-    * getAddress
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getAddress($iter=0)
+     * getAddress
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getAddress($iter = 0)
     {
         return $this->get('ADR', $iter);
     }
 
     /**
-    * getAgent
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getAgent($iter=0)
+     * getAgent
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getAgent($iter = 0)
     {
         return $this->get('AGENT', $iter);
     }
 
     /**
-    * getBirthday
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getBirthday($iter=0)
+     * getBirthday
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getBirthday($iter = 0)
     {
         return $this->get('BDAY', $iter);
     }
 
     /**
-    * getCategories
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getCategories($iter=0)
+     * getCategories
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getCategories($iter = 0)
     {
         return $this->get('CATEGORIES', $iter);
     }
 
     /**
-    * getClass
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getClass($iter=0)
+     * getClass
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getClass($iter = 0)
     {
         return $this->get('CLASS', $iter);
     }
 
     /**
-    * getEmail
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getEmail($iter=0)
+     * getEmail
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getEmail($iter = 0)
     {
         return $this->get('EMAIL', $iter);
     }
 
     /**
-    * getFormattedName
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getFormattedName($iter=0)
+     * getFormattedName
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getFormattedName($iter = 0)
     {
         return $this->get('FN', $iter);
     }
 
     /**
-    * getGeo
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getGeo($iter=0)
+     * getGeo
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getGeo($iter = 0)
     {
         return $this->get('GEO', $iter);
     }
 
     /**
-    * getKey
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getKey($iter=0)
+     * getKey
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getKey($iter = 0)
     {
         return $this->get('KEY', $iter);
     }
 
     /**
-    * getLabel
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getLabel($iter=0)
+     * getLabel
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getLabel($iter = 0)
     {
         return $this->get('LABEL', $iter);
     }
 
     /**
-    * getLogo
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getLogo($iter=0)
+     * getLogo
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getLogo($iter = 0)
     {
         return $this->get('LOGO', $iter);
     }
 
     /**
-    * getMailer
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getMailer($iter=0)
+     * getMailer
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getMailer($iter = 0)
     {
         return $this->get('MAILER', $iter);
     }
 
     /**
-    * getName
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getName($iter=0)
+     * getName
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getName($iter = 0)
     {
         return $this->get('N', $iter);
     }
 
     /**
-    * getNickname
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getNickname($iter=0)
+     * getNickname
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getNickname($iter = 0)
     {
         return $this->get('NICKNAME', $iter);
     }
 
     /**
-    * getNote
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getNote($iter=0)
+     * getNote
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getNote($iter = 0)
     {
         return $this->get('NOTE', $iter);
     }
 
     /**
-    * getOrganization
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getOrganization($iter=0)
+     * getOrganization
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getOrganization($iter = 0)
     {
         return $this->get('ORG', $iter);
     }
 
     /**
-    * getPhoto
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getPhoto($iter=0)
+     * getPhoto
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getPhoto($iter = 0)
     {
         return $this->get('PHOTO', $iter);
     }
 
     /**
-    * getProductID
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getProductID($iter=0)
+     * getProductID
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getProductID($iter = 0)
     {
         return $this->get('PRODID', $iter);
     }
 
     /**
-    * getRevision
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getRevision($iter=0)
+     * getRevision
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getRevision($iter = 0)
     {
         return $this->get('REV', $iter);
     }
 
     /**
-    * getRole
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getRole($iter=0)
+     * getRole
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getRole($iter = 0)
     {
         return $this->get('ROLE', $iter);
     }
 
     /**
-    * getSortString
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getSortString($iter=0)
+     * getSortString
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getSortString($iter = 0)
     {
         return $this->get('SORT-STRING', $iter);
     }
 
     /**
-    * getSound
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getSound($iter=0)
+     * getSound
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getSound($iter = 0)
     {
         return $this->get('SOUND', $iter);
     }
 
     /**
-    * getSource
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getSource($iter=0)
+     * getSource
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getSource($iter = 0)
     {
         return $this->get('SOURCE', $iter);
     }
 
     /**
-    * getSourceName
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getSourceName($iter=0)
+     * getSourceName
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getSourceName($iter = 0)
     {
         return $this->get('NAME', $iter);
     }
 
     /**
-    * getTZ
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getTZ($iter=0)
+     * getTZ
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getTZ($iter = 0)
     {
         return $this->get('TZ', $iter);
     }
 
     /**
-    * getTelephone
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getTelephone($iter=0)
+     * getTelephone
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getTelephone($iter = 0)
     {
         return $this->get('TEL', $iter);
     }
 
     /**
-    * getTitle
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getTitle($iter=0)
+     * getTitle
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getTitle($iter = 0)
     {
         return $this->get('TITLE', $iter);
     }
 
     /**
-    * getURL
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getURL($iter=0)
+     * getURL
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getURL($iter = 0)
     {
         return $this->get('URL', $iter);
     }
 
     /**
-    * getUniqueID
-    *
-    * @param int $iter iteration
-    *
-    * @return string
-    * @deprecated
-    * @see self::get()
-    */
-    public function getUniqueID($iter=0)
+     * getUniqueID
+     *
+     * @param int $iter iteration
+     *
+     * @return     string
+     * @deprecated
+     * @see        self::get()
+     */
+    public function getUniqueID($iter = 0)
     {
         return $this->get('UID', $iter);
     }
 
     /**
-    * setAgent
-    *
-    * @param string $val agent
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setAgent
+     *
+     * @param string $val agent
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setAgent($val)
     {
         return $this->set('AGENT', $val);
     }
 
     /**
-    * setBirthday
-    *
-    * @param string $val birthday
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setBirthday
+     *
+     * @param string $val birthday
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setBirthday($val)
     {
         return $this->set('BDAY', $val);
     }
 
     /**
-    * setClass
-    *
-    * @param string $val class
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setClass
+     *
+     * @param string $val class
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setClass($val)
     {
         return $this->set('CLASS', $val);
     }
 
     /**
-    * setFormattedName
-    *
-    * @param string $val formattedName
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setFormattedName
+     *
+     * @param string $val formattedName
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setFormattedName($val)
     {
         return $this->set('FN', $val);
     }
 
     /**
-    * setGeo
-    *
-    * @param string $lat latitude
-    * @param string $lon longitude
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setGeo
+     *
+     * @param string $lat latitude
+     * @param string $lon longitude
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setGeo()
     {
         $args = func_get_args();
@@ -1358,60 +1357,60 @@ public function setGeo()
     }
 
     /**
-    * setKey
-    *
-    * @param string $val key
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setKey
+     *
+     * @param string $val key
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setKey($val)
     {
         return $this->set('KEY', $val);
     }
 
     /**
-    * setLogo
-    *
-    * @param string $val logo
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setLogo
+     *
+     * @param string $val logo
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setLogo($val)
     {
         return $this->set('LOGO', $val);
     }
 
     /**
-    * setMailer
-    *
-    * @param string $val mailer
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setMailer
+     *
+     * @param string $val mailer
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setMailer($val)
     {
         return $this->set('MAILER', $val);
     }
 
     /**
-    * setName
-    *
-    * @param string $family family/last name.
-    * @param string $given  given/first name.
-    * @param string $addl   additional/middle name.
-    * @param string $prefix honorific prefix such as Mr., Miss, etc.
-    * @param string $suffix honorific suffix such as III, Jr., Ph.D., etc.
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setName
+     *
+     * @param string $family family/last name.
+     * @param string $given  given/first name.
+     * @param string $addl   additional/middle name.
+     * @param string $prefix honorific prefix such as Mr., Miss, etc.
+     * @param string $suffix honorific suffix such as III, Jr., Ph.D., etc.
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setName()
     {
         $args = func_get_args();
@@ -1419,191 +1418,184 @@ public function setName()
     }
 
     /**
-    * setNote
-    *
-    * @param string $val note
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setNote
+     *
+     * @param string $val note
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setNote($val)
     {
         return $this->set('NOTE', $val);
     }
 
     /**
-    * setPhoto
-    *
-    * @param string $val photo
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setPhoto
+     *
+     * @param string $val photo
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setPhoto($val)
     {
         return $this->set('PHOTO', $val);
     }
 
     /**
-    * setProductID
-    *
-    * @param string $val productID
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setProductID
+     *
+     * @param string $val productID
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setProductID($val)
     {
         return $this->set('PRODID', $val);
     }
 
     /**
-    * setRevision
-    *
-    * @param string $val revision
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setRevision
+     *
+     * @param string $val revision
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setRevision($val)
     {
         return $this->set('REV', $val);
     }
 
     /**
-    * setRole
-    *
-    * @param string $val role
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setRole
+     *
+     * @param string $val role
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setRole($val)
     {
         return $this->set('ROLE', $val);
     }
 
     /**
-    * setSortString
-    *
-    * @param string $val sortString
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setSortString
+     *
+     * @param string $val sortString
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setSortString($val)
     {
         return $this->set('SORT-STRING', $val);
     }
 
     /**
-    * setSound
-    *
-    * @param string $val sound
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setSound
+     *
+     * @param string $val sound
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setSound($val)
     {
         return $this->set('SOUND', $val);
     }
 
     /**
-    * setSource
-    *
-    * @param string $val source
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setSource
+     *
+     * @param string $val source
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setSource($val)
     {
         return $this->set('SOURCE', $val);
     }
 
     /**
-    * setSourceName
-    *
-    * @param string $val sourceName
-    *
-    * @return $this
-    *
-    * @deprecated
-    * @see self::set()
-    */
+     * setSourceName
+     *
+     * @param string $val sourceName
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setSourceName($val)
     {
         return $this->set('NAME', $val);
     }
 
     /**
-    * setTZ
-    *
-    * @param string $val TZ
-    *
-    * @return $this
-    *
-    * @deprecated
-    * @see self::set()
-    */
+     * setTZ
+     *
+     * @param string $val TZ
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setTZ($val)
     {
         return $this->set('TZ', $val);
     }
 
     /**
-    * setTitle
-    *
-    * @param string $val title
-    *
-    * @return $this
-    *
-    * @deprecated
-    * @see self::set()
-    */
+     * setTitle
+     *
+     * @param string $val title
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setTitle($val)
     {
         return $this->set('TITLE', $val);
     }
 
     /**
-    * setURL
-    *
-    * @param string $val URL
-    *
-    * @return $this
-    *
-    * @deprecated
-    * @see self::set()
-    */
+     * setURL
+     *
+     * @param string $val URL
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setURL($val)
     {
         return $this->set('URL', $val);
     }
 
     /**
-    * setUniqueID
-    *
-    * @param string $val uniqueID
-    *
-    * @return $this
-    * @deprecated
-    * @see self::set()
-    */
+     * setUniqueID
+     *
+     * @param string $val uniqueID
+     *
+     * @return     $this
+     * @deprecated
+     * @see        self::set()
+     */
     public function setUniqueID($val)
     {
         return $this->set('UID', $val);
     }
-
 }
-
-?>
diff --git a/tests/File/IMC/BugsTest.php b/tests/File/IMC/BugsTest.php
index bccec87..1bc243a 100644
--- a/tests/File/IMC/BugsTest.php
+++ b/tests/File/IMC/BugsTest.php
@@ -18,12 +18,6 @@
  * @link      http://pear.php.net/package/File_IMC
  */
 
-/**
- * PHPUnit_Framework_TestCase
- * @ignore
- */
-require_once 'PHPUnit/Framework/TestCase.php';
-
 /**
  * File_IMC
  */
diff --git a/tests/File/IMC/BuildTest.php b/tests/File/IMC/BuildTest.php
index 0ab899c..764c08b 100644
--- a/tests/File/IMC/BuildTest.php
+++ b/tests/File/IMC/BuildTest.php
@@ -18,12 +18,6 @@
  * @link      http://pear.php.net/package/File_IMC
  */
 
-/**
- * PHPUnit_Framework_TestCase
- * @ignore
- */
-require_once 'PHPUnit/Framework/TestCase.php';
-
 /**
  * File_IMC
  */
diff --git a/tests/File/IMC/Parse/VcalendarTest.php b/tests/File/IMC/Parse/VcalendarTest.php
index 7f170f8..9d67087 100644
--- a/tests/File/IMC/Parse/VcalendarTest.php
+++ b/tests/File/IMC/Parse/VcalendarTest.php
@@ -18,12 +18,6 @@
  * @link     http://pear.php.net/package/File_IMC
  */
 
-/**
- * PHPUnit_Framework_TestCase
- * @ignore
- */
-require_once 'PHPUnit/Framework/TestCase.php';
-
 /**
  * File_IMC
  */
diff --git a/tests/File/IMC/ParseTest.php b/tests/File/IMC/ParseTest.php
index d4ea7cd..d95503f 100644
--- a/tests/File/IMC/ParseTest.php
+++ b/tests/File/IMC/ParseTest.php
@@ -18,12 +18,6 @@
  * @link      http://pear.php.net/package/File_IMC
  */
 
-/**
- * PHPUnit_Framework_TestCase
- * @ignore
- */
-require_once 'PHPUnit/Framework/TestCase.php';
-
 /**
  * File_IMC
  */