Changeset 6db528a


Ignore:
Timestamp:
06/21/10 16:10:06 (2 years ago)
Author:
maparent <maparent@…>
Branches:
('master', '8434b8322dd6f8ff37c17bd45b94e7a826f86b53')('vitrine', 'afe109c61a407808bd54d88bcccb1cde726a010a')
Children:
f7fcb8f42bc54078fe4c73eb63af5fcf75f308cd
Parents:
681f10fb3c57d071a4c02e5371cbc6136f4be4a4
git-author:
maparent <maparent@9a151f2f-a8df-0310-a5c5-9fb547312938>2010-06-21 20:10:06+00:00
git-committer:
maparent <maparent@9a151f2f-a8df-0310-a5c5-9fb547312938>2010-06-21 20:10:06+00:00
Message:

New VCard parsing function

git-svn-id: http://projects.coeus.ca/svn/eureka@576 9a151f2f-a8df-0310-a5c5-9fb547312938

Location:
trunk/html
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/html/classes/VCard.php

    r32dece9 r6db528a  
    2929error_reporting(E_ALL); 
    3030 
     31/** Représente une ligne d'une VCard. 
     32 */ 
     33 
     34class VCardLineInfo { 
     35    const line_r = '/^(\s*)(?:(?P<grp>[A-Za-z][-0-9A-Za-z]*)\.)?(?P<nm>[A-Za-z][-0-9A-Za-z]*)(?P<pa>(?:;(?:AIFF|AOL|APPLELINK|ATTMAIL|AVI|BBS|BMP|CAR|CELL|CGM|CIS|DIB|DOM|EWORLD|FAX|GIF|HOME|IBMMAIL|INTERNET|INTL|ISDN|JPEG|MCIMAIL|MET|MODEM|MPEG|MPEG2|MSG|PAGER|PARCEL|PCM|PDF|PGP|PICT|PMB|POSTAL|POWERSHARE|PREF|PRODIGY|PS|QTIME|TIFF|TLX|VIDEO|VOICE|WAVE|WMF|WORK|X400|X509|(?:[A-Za-z][-0-9A-Za-z]*=(?:"[ \t!#-\x7e\x80-\xff]*"|[ \t!#-+\.-9<-~\x80-\xff]*)(?:,(?:"[ \t!#-\x7e\x80-\xff]*"|[ \t!#-+\.-9<-~\x80-\xff]*))*)))*):(?P<va>[ \t!-~\x80-\xff]*)$/'; 
     36    const line_start_r = '/(\s*)(?:(?P<grp>[A-Za-z][-0-9A-Za-z]*)\.)?(?P<nm>[A-Za-z][-0-9A-Za-z]*)[:;]/'; 
     37    const base64_r = '|^\s*([0-9A-Za-z\+\\/\\\\]+)(={0,2})\s*$|'; 
     38    private static $encodings = array('B' => true, '7BIT' => true, '8BIT' => true, 
     39        'QUOTED-PRINTABLE' => true, 'BASE64' => true); 
     40    private static $name_allows_x = array('MAILER' => true, 'ADR' => true, 'TITLE' => true, 
     41        'SORT-STRING' => true, 'LABEL' => true, 'NOTE' => true, 
     42        'SOURCE' => true, 'ROLE' => true, 'FN' => true, 'ORG' => true, 
     43        'N' => true, 'NICKNAME' => true, 'CATEGORIES' => true); 
     44    private static $legal_params=array( 
     45        'ADR' => array('TYPE' => true,'VALUE' => true,'LANGUAGE' => true), 
     46        'AGENT' => array('VALUE' => true), 
     47        'BEGIN' => array(), 
     48        'BDAY' => array('VALUE' => true), 
     49        'CATEGORIES' => array('VALUE' => true,'LANGUAGE' => true), 
     50        'CLASS' => array(), 
     51        'EMAIL' => array('TYPE' => true), 
     52        'END' => array(), 
     53        'FN' => array('VALUE' => true,'LANGUAGE' => true), 
     54        'GEO' => array(), 
     55        'KEY' => array('TYPE' => true,'ENCODING' => true), 
     56        'LABEL' => array('TYPE' => true,'VALUE' => true,'LANGUAGE' => true), 
     57        'LOGO' => array('VALUE' => true,'ENCODING' => true,'TYPE' => true), 
     58        'MAILER' => array('VALUE' => true,'LANGUAGE' => true), 
     59        'N' => array('VALUE' => true,'LANGUAGE' => true), 
     60        'NAME' => array(), 
     61        'NICKNAME' => array('VALUE' => true,'LANGUAGE' => true), 
     62        'NOTE' => array('VALUE' => true,'LANGUAGE' => true), 
     63        'ORG' => array('VALUE' => true,'LANGUAGE' => true), 
     64        'PHOTO' => array('VALUE' => true,'ENCODING' => true,'TYPE' => true), 
     65        'PRODID' => array(), 
     66        'PROFILE' => array(), 
     67        'REV' => array('VALUE' => true), 
     68        'ROLE' => array('VALUE' => true,'LANGUAGE' => true), 
     69        'SORT-STRING' => array('VALUE' => true,'LANGUAGE' => true), 
     70        'SOUND' => array('VALUE' => true,'ENCODING' => true,'TYPE' => true), 
     71        'SOURCE' => array('VALUE' => true,'CONTEXT' => true), 
     72        'TEL' => array('TYPE' => true), 
     73        'TITLE' => array('VALUE' => true,'LANGUAGE' => true), 
     74        'TZ' => array(), 
     75        'UID' => array(), 
     76        'URL' => array(), 
     77        'VERSION' => array()); 
     78    private static $param_values = array( 
     79        'VALUE' => '/BINARY|CID|CONTENT\-ID|DATE|DATE\-TIME|INLINE|PTEXT|URI|URL|X\-[!-\+\-\/-9;<\?-Z\\^-~]+/', 
     80        'TYPE' => '/BBS|CAR|CELL|DOM|FAX|HOME|INTERNET|INTL|ISDN|MODEM|MSG|PAGER|PARCEL|PCS|PGP|POSTAL|PREF|VIDEO|VOICE|WORK|X509|X\-[!-\+\-\/-9;<\?-Z\\^-~]+/', 
     81        'ENCODING' => '/7BIT|8BIT|B|BASE64|QUOTED\-PRINTABLE|X\-[!-\+\-\/-9;<\?-Z\\^-~]+/', 
     82        'LANGUAGE' => '/[a-z]{2-3}(\-[A-Z]+)/', 
     83        'CHARSET' => '/\w+/' 
     84    ); 
     85    private static $props_structured = array( 
     86        'N' => array('family_name', 'first_name', 'additional_name', 'hon_prefixes', 'hon_suffixes'), 
     87        'ADR' => array('adr_pob', 'adr_ext', 'adr_stradr', 'adr_loc', 'adr_reg', 'adr_pc', 'adr_ctry'), 
     88        'GEO' => array('latitude', 'longitude'), 
     89        'ORG' => array() 
     90    ); 
     91 
     92    private $_is_valid_start = false; 
     93    private $_bad_end = true; 
     94    private $name = ''; 
     95    private $group = ''; 
     96    private $value = ''; 
     97    private $params = array(); 
     98    private $errors = array(); 
     99    public  $rawline; 
     100 
     101    function __construct($line) { 
     102        $this->rawline = $line; 
     103        $match = array(); 
     104        if (0 != preg_match(self::line_start_r, $line, $match)) { 
     105            $name = strtoupper($match['nm']); 
     106            $this->_is_valid_start = array_key_exists($name, self::$legal_params) or $this->is_xname($name); 
     107            $this->name = $name; 
     108            $this->group = $match['grp']; 
     109            $this->tryLine(); 
     110        } 
     111    } 
     112 
     113    function splitEscapedStructure($s, $c=';') { 
     114        $parts = explode($c, $s); 
     115        $new_parts = array(); 
     116        $acc = ''; 
     117        foreach ($parts as $part) { 
     118            if (strlen($part) != 0 and substr($part, strlen($part)-1,1) == '\\') { 
     119                $acc .= $part.$c; 
     120            } else { 
     121                $new_parts[] = $acc.$part; 
     122                $acc = ''; 
     123            } 
     124        } 
     125        return $new_parts; 
     126    } 
     127 
     128    function is_xname($s) { 
     129        return strncmp(strtoupper($s), 'X-', 2) == 0; 
     130    } 
     131 
     132    function getEncoding() { 
     133        if (array_key_exists('ENCODING', $this->params)) { 
     134            $encoding = $this->params['ENCODING']; 
     135            $encoding = strtoupper($encoding[0]); 
     136            if ($encoding == 'BASE64') { 
     137                return 'B'; 
     138            } elseif (array_key_exists($encoding, self::$encodings)) { 
     139                return $encoding; 
     140            } 
     141        } 
     142        return null; 
     143    } 
     144 
     145    function checkParams($paramList) { 
     146        $params = array(); 
     147        if ('' != $paramList) { 
     148            $paramList = $this->splitEscapedStructure($paramList); 
     149            foreach ($paramList as $param) { 
     150                if ('' == $param) { 
     151                    continue; 
     152                } 
     153                if (strpos($param, '=') !== false) { 
     154                    list($key,$vals) = explode('=',$param, 2); 
     155                } else {  # From known_types 
     156                    $key = 'TYPE'; 
     157                    $vals = $param; 
     158                } 
     159                $vals = $this->splitEscapedStructure($vals,','); 
     160                $key = strtoupper($key); 
     161                if (array_key_exists($key, $params)) { 
     162                    $params[$key] = array_merge($params[$key], $vals); 
     163                } else { 
     164                    $params[$key] = $vals; 
     165                } 
     166                $valid_params = array(); 
     167                if (array_key_exists($this->name, self::$legal_params)) { 
     168                    $valid_params = self::$legal_params[$this->name]; 
     169                } 
     170                if (!array_key_exists($key, $valid_params)) { 
     171                    if (!($this->is_xname($key) and ($this->is_xname($this->name) or array_key_exists($this->name, self::$name_allows_x)))) { 
     172                        $this->errors[] = sprintf("ERROR Illegal param: %s for %s", $key, $this->name); 
     173                    } 
     174                    if (array_key_exists($key, self::$param_values)) { 
     175                        foreach ($vals as $v) { 
     176                            if (0 == preg_match(self::$param_values[$key], $v)) { 
     177                                $this->errors[] = sprintf("ERROR Ilegal param value: %s for %s", $v, $key); 
     178                            } 
     179                        } 
     180                    } 
     181                } 
     182            } 
     183        } 
     184        return $params; 
     185    } 
     186 
     187    function tryLine() { 
     188        $match = array(); 
     189        if (0 != preg_match(self::line_r,$this->rawline, $match)) { 
     190            $this->_bad_end = false; 
     191            $this->value = $match['va']; 
     192            $this->params = $this->checkParams($match['pa']); 
     193        } else { 
     194            $this->_bad_end = true; 
     195            # possibly: the params go beyond a simple line 
     196            $this->value = ''; 
     197            $this->params = array(); 
     198        } 
     199    } 
     200 
     201    function canAppend($line, $indented=false) { 
     202        if ($this->getEncoding() == 'B' and 0 != preg_match(self::base64_r, $line)) { 
     203            return true; 
     204        } 
     205        return $indented; 
     206    } 
     207    function append($line) { 
     208        if ($this->_bad_end) { 
     209            $this->rawline .= $line; 
     210            $this->tryLine(); 
     211        } else { 
     212            $this->value .= $line; 
     213        } 
     214        return true; # TODO: Do I need this? 
     215    } 
     216    function getResults(&$results, &$errors) { 
     217        if ($this->_bad_end or !$this->_is_valid_start) { 
     218            $errors[] = "ERROR Invalid line: ".$this->rawline; 
     219            return; 
     220        } 
     221        if ($this->getEncoding() == 'B') { 
     222            $value = base64_decode($this->value); 
     223            if ($value === FALSE) { 
     224                $errors[] = "ERROR Base64 error"; 
     225            } else { 
     226                $results[] = array($this->name.'_data', $this->group, $this->params, $value); 
     227                if ($this->name == 'logo' or $this->name == 'photo') { 
     228                    $type = 'jpeg'; 
     229                    if (array_key_exists('TYPE',$this->params)) { 
     230                        $type = $this->params['TYPE']; 
     231                    } 
     232                    $results[] = array($this->name.'_type', $this->group, $this->params, 'image/'.$type); 
     233                } 
     234            } 
     235        } 
     236        else { 
     237            if ($this->getEncoding() == 'QUOTED-PRINTABLE') { 
     238                $this->value = quoted_printable_decode($this->value); 
     239            } 
     240            if (array_key_exists($this->name, self::$props_structured)) { 
     241                $parts = $this->splitEscapedStructure($this->value, ';'); 
     242                for ($i = 0; $i<count($parts);$i++) { 
     243                    $parts[$i] = VCard::vcardUnescape($parts[$i]); 
     244                } 
     245                $part_names = self::$props_structured[$this->name]; 
     246                if (0 != count($part_names)) { 
     247                    if (count($parts) > count($part_names)) { 
     248                        $this->errors[] = sprintf("ERROR Too many parts in %s:%s", 
     249                            $this->name, $this->value); 
     250                    } 
     251                    $num = 0; 
     252                    $avalue = array(); 
     253                    foreach ($part_names as $part_name) { 
     254                        if ($num < count($parts)) { 
     255                            $v = $parts[$num++]; 
     256                        } else { 
     257                            $v = ''; 
     258                        } 
     259                        if ($v != '') { 
     260                            $avalue[$part_name] = $v; 
     261                        } 
     262                    } 
     263                    $this->value = $avalue; 
     264                } else { 
     265                    $this->value = $parts; 
     266                } 
     267            } else { 
     268                $this->value = VCard::vcardUnescape($this->value); 
     269            } 
     270        } 
     271        if ($this->name == 'VERSION' and trim($this->value) != '3.0') { 
     272            $errors[] = 'ERROR wrong Vcard version: '.$this->value; 
     273        } 
     274        $errors = array_merge($errors, $this->errors); 
     275        $results[] = array($this->name, $this->group, $this->params, $this->value); 
     276    } 
     277    function hasErrors() { 
     278        return $this->_bad_end or count($this->errors) > 0 or !$this->_is_valid_start; 
     279    } 
     280    function isValidStart() { 
     281        return $this->_is_valid_start; 
     282    } 
     283    function isBegin() { 
     284        return $this->name == 'BEGIN' and $this->group == null and $this->value == 'VCARD' and !$this->params; 
     285    } 
     286    function isEnd() { 
     287        return $this->name == 'END' and $this->group == null and $this->value == 'VCARD' and !$this->params; 
     288    } 
     289 
     290    function isDummy() { 
     291        return false; 
     292    } 
     293} 
     294 
     295/** Représente la ligne initiale, vide. 
     296 */ 
     297class VCardDummyLineInfo { 
     298    public $rawline = ''; 
     299    function hasErrors() { 
     300        return False; 
     301    } 
     302    function getResults(&$results, &$errors) { 
     303    } 
     304    function canAppend($line, $indented=False) { 
     305        return False; 
     306    } 
     307    function isEnd() { 
     308        return False; 
     309    } 
     310    function isDummy() { 
     311        return True; 
     312    } 
     313} 
     314 
    31315/** Représente une VCard en particulier.  
    32316 */ 
     
    34318define('VCARD_NUM_PICTURE', 2); //Define the type of my picture 
    35319class VCard extends GenericDataObject { 
     320    private static $legal_fields = array('FN' => true, 'ORG' => true, 'TITLE' => true, 
     321        'ROLE' => true, 'EMAIL' => true, 'TEL' => true, 'FAX' => true, 'ADR_POB' => true, 
     322        'ADR_EXT' => true, 'ADR_STRADR' => true, 'ADR_LOC' => true, 'ADR_REG' => true, 
     323        'ADR_PC' => true, 'ADR_CTRY' => true, 'PHOTO_URL' => true, 'PHOTO_TYPE' => true, 
     324        'PHOTO_data' => true, 'N' => true); 
    36325    private $mRow; 
    37326    /** Get an instance of the object 
     
    14221711     *          @param           $str string containing a SINGLE vCard in vCard 3.0 format. 
    14231712     * If it contains more than one, only the first one will be imported 
     1713     * @return An array with two elements. First is an array of error strings. 
     1714     * Second is an array of supported vCard elements, each of the following form: 
     1715     * ($name, $group, $params, $value) 
     1716     * Params are an array Name=>array of values 
     1717     * $value may be an array in some cases (ORG, ADR, N, GEO) 
     1718     */ 
     1719    public static function full_parse($vcard) { 
     1720        $errors = array(); 
     1721        $minSpaces = 30000; 
     1722        $results = array(); 
     1723        $oldInfo = new VCardDummyLineInfo(); 
     1724        if (strpos($vcard, "\r") !== false) { 
     1725            $vcard = str_replace("\r\n" , "\n" , $vcard); 
     1726            $vcard = str_replace("\r" , "\n" , $vcard); 
     1727        } 
     1728        $lineNum = 0; 
     1729        foreach (explode("\n",$vcard) as $l) { 
     1730            $info = new VCardLineInfo($l); 
     1731            $numSpaces = strlen($l) - strlen(ltrim($l)); 
     1732            if ($lineNum > 0) { 
     1733                $minSpaces = min($minSpaces, $numSpaces); 
     1734            } 
     1735            if (!$info->hasErrors()) { 
     1736                if ($lineNum == 0 and !$info->isBegin()) { 
     1737                    $errors[] = "ERROR Does not start with begin: ".$l; 
     1738                } 
     1739                $oldInfo->getResults($results, $errors); 
     1740                $oldInfo = $info; 
     1741            } elseif ($oldInfo->canAppend($l, $numSpaces > $minSpaces)) { 
     1742                $oldInfo->append(substr($l,$minSpaces+1)); 
     1743            } elseif ($info->isValidStart()) { 
     1744                $oldInfo->getResults($results, $errors); 
     1745                $oldInfo = $info; 
     1746            } elseif ($oldInfo->canAppend($l, True)) { 
     1747                if (strlen($l) > 0) { 
     1748                    $errors[]="WARNING Not indented: ".$l; 
     1749                    if ($minSpaces < strlen($l) and false === strpos(" \t", substr($l,$minSpaces,1))) { 
     1750                        $l = ' '.$l; 
     1751                    } 
     1752                    $oldInfo->append(substr($l, $minSpaces)); 
     1753                } 
     1754            } else { 
     1755                $oldInfo->getResults($results, $errors); 
     1756                $oldInfo = $info; 
     1757            } 
     1758            $lineNum++; 
     1759        } 
     1760        $oldInfo->getResults($results, $errors); 
     1761        if (count($results) == 0) { 
     1762            $errors[] = "ERROR Empty VCARD"; 
     1763        } 
     1764        elseif (!$oldInfo->isEnd()) { 
     1765            $errors[]="ERROR Does not end with END: ".$oldInfo->rawline; 
     1766        } 
     1767        return array($errors, $results); 
     1768    } 
     1769     
     1770    /** Parses a vCard into a tokenised array 
     1771     *          @param           $str string containing a SINGLE vCard in vCard 3.0 format. 
     1772     * If it contains more than one, only the first one will be imported 
    14241773     * @return An array of supported vCard elements, or an empty array if something 
    14251774     * went wrong 
    14261775     */ 
    1427     public static function parse($str) { 
     1776    public static function parse($vcard) { 
     1777        list($errors, $full_results) = self::full_parse($vcard); 
     1778        // errors are discarded. 
     1779        $results = array(); 
     1780        foreach ($full_results as $r) { 
     1781            list($name, $group, $params, $value) = $r; 
     1782            if (!array_key_exists($name, self::$legal_fields)) { 
     1783                continue; 
     1784            } 
     1785            if ($name == 'TEL') { 
     1786                if (array_search('WORK',$params['TYPE']) != FALSE) { 
     1787                    if (array_search('FAX',$params['TYPE']) != FALSE) { 
     1788                        $results['fax'] = $value; 
     1789                    } elseif (array_search('VOICE',$this->params['TYPE']) != FALSE) { 
     1790                        $results['tel'] = $value; 
     1791                    } 
     1792                } 
     1793                // suppress non-work phone? Seems so. 
     1794                continue; 
     1795            } elseif ($name == 'ORG') { 
     1796                $value = implode(';',$value); 
     1797            } elseif (is_array($value)) { 
     1798                foreach ($value as $k=>$v) { 
     1799                    $results[strtolower($k)] = $v; 
     1800                } 
     1801            } 
     1802            if ($group != '') { 
     1803                $name = $group . '.' . $name; 
     1804            } 
     1805            $results[strtolower($name)] = $value; 
     1806        } 
     1807        return $results; 
     1808    } 
     1809 
     1810    /** Parses a vCard into a tokenised array 
     1811     *          @param           $str string containing a SINGLE vCard in vCard 3.0 format. 
     1812     * If it contains more than one, only the first one will be imported 
     1813     * @return An array of supported vCard elements, or an empty array if something 
     1814     * went wrong 
     1815     */ 
     1816    public static function old_parse($str) { 
    14281817        $vcard_elements = array (); 
    14291818        //echo "VCard::parse(): vCard raw data:<br><pre>"; print_r($str); echo "</pre>"; 
  • trunk/html/tests/vcardTest.php

    rf90da8f r6db528a  
    3838    } 
    3939 
     40    private static function extractField($vcard, $name, $group=null) { 
     41        foreach ($vcard as $line) { 
     42            list($lname, $lgroup, $param, $value) = $line; 
     43            if ($name == $lname and $group == $lgroup) { 
     44                return array($param, $value); 
     45            } 
     46        } 
     47        return null; 
     48    } 
     49 
     50    const simple_card = 'BEGIN:VCARD 
     51VERSION:3.0 
     52FN:Marc-Antoine Parent 
     53N:PARENT;Marc-Antoine 
     54TEL;TYPE=WORK,PREF:(123) 456-7890 
     55ADR;TYPE=WORK:;;1234 rue Marue;Maville;Maprovince;;Lhiver 
     56EMAIL;TYPE=INTERNET:maparent@exemple.com 
     57END:VCARD'; 
     58 
     59    public function testFindFN() { 
     60        list($errors, $res) = VCard::full_parse(self::simple_card); 
     61        $this->assertEquals(count($errors),0); 
     62        $field = self::extractField($res, 'FN'); 
     63        $this->assertEquals($field[1], 'Marc-Antoine Parent'); 
     64    } 
     65 
     66    const comma_param_card = 'BEGIN:VCARD 
     67VERSION:3.0 
     68FN:Marc-Antoine Parent 
     69N:PARENT;Marc-Antoine 
     70TEL;TYPE=WORK,PREF:(123) 456-7890 
     71ADR;TYPE=WORK:;;1234 rue Marue;Maville;Maprovince;;Lhiver 
     72EMAIL;TYPE=INTERNET,pref:maparent@exemple.com 
     73END:VCARD'; 
     74 
     75    public function testCommaParam() { 
     76        list($errors, $res) = VCard::full_parse(self::comma_param_card); 
     77        $this->assertEquals(count($errors),0); 
     78        $field = self::extractField($res, 'EMAIL'); 
     79        $this->assertEquals($field[0]['TYPE'], array('INTERNET', 'pref')); 
     80    } 
     81 
     82    const repeat_param_card = 'BEGIN:VCARD 
     83VERSION:3.0 
     84FN:Marc-Antoine Parent 
     85N:PARENT;Marc-Antoine 
     86TEL;TYPE=WORK,PREF:(123) 456-7890 
     87ADR;TYPE=WORK:;;1234 rue Marue;Maville;Maprovince;;Lhiver 
     88EMAIL;TYPE=INTERNET;TYPE=pref:maparent@exemple.com 
     89END:VCARD'; 
     90 
     91    public function testRepeatParam() { 
     92        list($errors, $res) = VCard::full_parse(self::repeat_param_card); 
     93        $this->assertEquals(count($errors),0); 
     94        $field = self::extractField($res, 'EMAIL'); 
     95        $this->assertEquals($field[0]['TYPE'], array('INTERNET', 'pref')); 
     96    } 
     97 
     98    const continuation_card = 'BEGIN:VCARD 
     99VERSION:3.0 
     100FN:Marc-Antoine Parent 
     101N:PARENT;Marc-Antoine 
     102TEL;TYPE=WORK,PREF:(123) 456-7890 
     103ADR;TYPE=WORK:;;1234 rue Marue; 
     104  Maville;Maprovince;;Lhiver 
     105EMAIL;TYPE=INTERNET:maparent@exemple.com 
     106END:VCARD'; 
     107 
     108    public function testContinuation() { 
     109        list($errors, $res) = VCard::full_parse(self::continuation_card); 
     110        $this->assertEquals(count($errors),0); 
     111        $field = self::extractField($res, 'ADR'); 
     112        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue'); 
     113    } 
     114 
     115    const unindented_continuation_card = 'BEGIN:VCARD 
     116VERSION:3.0 
     117FN:Marc-Antoine Parent 
     118N:PARENT;Marc-Antoine 
     119TEL;TYPE=WORK,PREF:(123) 456-7890 
     120ADR;TYPE=WORK:;;1234 rue Marue; 
     121Maville;Maprovince;;Lhiver 
     122EMAIL;TYPE=INTERNET:maparent@exemple.com 
     123END:VCARD'; 
     124 
     125    const globally_indented_card = 'BEGIN:VCARD 
     126    VERSION:3.0 
     127    FN:Marc-Antoine Parent 
     128    N:PARENT;Marc-Antoine 
     129    TEL;TYPE=WORK,PREF:(123) 456-7890 
     130    ADR;TYPE=WORK:;;1234 rue Marue; 
     131    Maville;Maprovince;;Lhiver 
     132    EMAIL;TYPE=INTERNET:maparent@exemple.com 
     133    END:VCARD'; 
     134 
     135    const globally_indented_with_reindented_continuation_card = 'BEGIN:VCARD 
     136    VERSION:3.0 
     137    FN:Marc-Antoine Parent 
     138    N:PARENT;Marc-Antoine 
     139    TEL;TYPE=WORK,PREF:(123) 456-7890 
     140    ADR;TYPE=WORK:;;1234 rue Marue; 
     141     Maville;Maprovince;;Lhiver 
     142    EMAIL;TYPE=INTERNET:maparent@exemple.com 
     143    END:VCARD'; 
     144 
     145 
     146    public function testUnindentedContinuation() { 
     147        list($errors, $res) = VCard::full_parse(self::unindented_continuation_card); 
     148        # there should be a WARNING 
     149        $this->assertEquals(count($errors), 1); 
     150        $this->assertSame(strpos($errors[0],'WARNING'), 0); 
     151        $field = self::extractField($res, 'ADR'); 
     152        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue'); 
     153    } 
     154 
     155    public function testGloballyIndented() { 
     156        list($errors, $res) = VCard::full_parse(self::globally_indented_card); 
     157        $this->assertEquals(count($errors), 1); 
     158        $this->assertSame(strpos($errors[0],'WARNING'), 0); 
     159        $field = self::extractField($res, 'ADR'); 
     160        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue'); 
     161    } 
     162 
     163    public function testGloballyIndentedWithReindentedContinuation() { 
     164        list($errors, $res) = VCard::full_parse(self::globally_indented_with_reindented_continuation_card); 
     165        $this->assertEquals(count($errors),0); 
     166        $field = self::extractField($res, 'ADR'); 
     167        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue'); 
     168    } 
     169 
     170    const invalid_line_card = 'BEGIN:VCARD 
     171VERSION:3.0 
     172FN:Marc-Antoine Parent 
     173N:PARENT;Marc-Antoine 
     174TEL;TYPE=WORK,PREF:(123) 456-7890 
     175ADR;TYPE=WORK:;;1234 rue Marue;Maville;Maprovince;;Lhiver 
     176PHOTO;VALUE= 
     177EMAIL;TYPE=INTERNET:maparent@exemple.com 
     178END:VCARD'; 
     179 
     180    public function testInvalidLine() { 
     181        list($errors, $res) = VCard::full_parse(self::invalid_line_card); 
     182        # there should be a WARNING 
     183        $this->assertEquals(count($errors), 1); 
     184        $this->assertSame(strpos($errors[0],'ERROR Invalid line'), 0); 
     185        $field = self::extractField($res, 'ADR'); 
     186        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue'); 
     187    } 
     188 
     189    const rapport_nc_1_card = 'BEGIN:VCARD 
     190VERSION:3.0 
     191FN:Bruno BASTARD 
     192N:BASTARD;Bruno 
     193PHOTO;VALUE= 
     194TEL;TYPE=WORK,PREF:NR 
     195ADR;TYPE=WORK:;Université de Toulouse-Le Mirail 
     196Centre Audiovisuel et multimédia 
     1975 allées Antonio Machado 
     19831058 Toulouse Cedex 01;toulouse;;31000;FR 
     199EMAIL;TYPE=INTERNET:NR 
     200ROLE:content provider 
     201ORG:NR; 
     202URL:NR 
     203END:VCARD'; 
     204 
     205    public function testRapportNC1() { 
     206        list($errors, $res) = VCard::full_parse(self::rapport_nc_1_card); 
     207        $field = self::extractField($res, 'ADR'); 
     208        $indent_warning = 0; 
     209        $invalid_error = 0; 
     210        $other = 0; 
     211        foreach ($errors as $error) { 
     212            if (strpos($error,'ERROR Invalid line') === 0) { 
     213                $invalid_error++; 
     214            } elseif (strpos($error,'WARNING Not indented') === 0) { 
     215                $indent_warning++; 
     216            } else { 
     217                $other++; 
     218            } 
     219        } 
     220        $this->assertEquals($invalid_error, 1); 
     221        $this->assertEquals($indent_warning, 3); 
     222        $this->assertEquals($other, 0); 
     223        $this->assertEquals($field[1]['adr_ext'], 'Université de Toulouse-Le Mirail Centre Audiovisuel et multimédia 5 allées Antonio Machado 31058 Toulouse Cedex 01'); 
     224    } 
     225 
     226    const rapport_nc_2_card = 'BEGIN:VCARD 
     227    VERSION:3.0 
     228    ORG:MinistÚre de l\'Éducation nationale, de l\'Enseignement 
     229    supérieur et de la Recherche 
     230    END:VCARD'; 
     231 
     232    public function testRapportNC2() { 
     233        list($errors, $res) = VCard::full_parse(self::rapport_nc_2_card); 
     234        $field = self::extractField($res, 'ORG'); 
     235        $this->assertEquals($field[1], array('MinistÚre de l\'Éducation nationale, de l\'Enseignement supérieur et de la Recherche')); 
     236        $this->assertEquals(count($errors),1); 
     237        $this->assertSame(strpos($errors[0],'WARNING'), 0); 
     238    } 
     239 
     240    public function testUnnamedParam() { 
     241        list($errors, $res) = VCard::full_parse('BEGIN:VCARD 
     242FN:Susan Thomas 
     243TEL:+1-919-555-1234 
     244EMAIL;INTERNET:sthomas@host.com 
     245END:VCARD'); 
     246        $this->assertEquals(count($errors),0); 
    40247} 
     248 
     249    const agent_card = 'BEGIN:VCARD 
     250VERSION:3.0 
     251FN:Bruno BASTARD 
     252N:BASTARD;Bruno 
     253AGENT:BEGIN:VCARD\\nFN:Susan Thomas\\nTEL:+1-919-555- 
     254 1234\\nEMAIL\\;TYPE=INTERNET:sthomas@host.com\\nEND:VCARD\\n 
     255END:VCARD'; 
     256 
     257    public function testAgent() { 
     258        list($errors, $res) = VCard::full_parse(self::agent_card); 
     259        $this->assertEquals(count($errors),0); 
     260        $agent = self::extractField($res, 'AGENT'); 
     261        $agent = $agent[1]; 
     262        $this->assertEquals($agent, 
     263        "BEGIN:VCARD\nFN:Susan Thomas\nTEL:+1-919-555-1234\nEMAIL;TYPE=INTERNET:sthomas@host.com\nEND:VCARD\n"); 
     264         
     265        $agent = str_replace(array('\\;','\\n'),array(';',"\n"),$agent); 
     266        list($errors, $res) = VCard::full_parse($agent); 
     267        $field = self::extractField($res, 'TEL'); 
     268        $this->assertEquals($field[1],'+1-919-555-1234'); 
     269        $this->assertEquals(count($errors),0); 
     270    } 
     271 
     272 
     273##Cas horrible: Les paramÚtres débordent sur la ligne suivante 
     274 
     275    const param_continuation_card = 'BEGIN:VCARD 
     276VERSION:3.0 
     277FN:Marc-Antoine Parent 
     278N:PARENT;Marc-Antoine 
     279TEL;TYPE=HOME,WORK,PREF,VOICE,FAX,MSG,CELL,PAGER,BBS,MODEM, 
     280 CAR,ISDN,VIDEO,PCS:(123) 456-7890 
     281END:VCARD'; 
     282 
     283    public function testParamContinuation() { 
     284        list($errors, $res) = VCard::full_parse(self::param_continuation_card); 
     285        $this->assertEquals(count($errors),0); 
     286        $field = self::extractField($res, 'TEL'); 
     287        $this->assertEquals(count($field[0]['TYPE']), 14); 
     288    } 
     289 
     290# Autre cas: paramÚtres ou autres contenant des séparateurs escapés 
     291    const escaped_comma_card = 'BEGIN:VCARD 
     292VERSION:3.0 
     293FN:Marc-Antoine Parent 
     294N:PARENT;Marc-Antoine 
     295TEL;TYPE=HOME,X-COMMA\,VALUE:(123) 456-7890 
     296END:VCARD'; 
     297# Echec pcq les paramÚtres n'ont pas droit aux séparateurs 
     298 
     299    const escaped_structured_value_card = 'BEGIN:VCARD 
     300VERSION:3.0 
     301FN:Marc-Antoine Parent 
     302N:PARENT;Marc-Antoine 
     303ADR;TYPE=WORK:;;1234 rue Marue\\;bureau 100;Maville;Maprovince;;Lhiver 
     304END:VCARD'; 
     305    public function testEscapedStructured() { 
     306        list($errors, $res) = VCard::full_parse(self::escaped_structured_value_card); 
     307        $this->assertEquals(count($errors),0); 
     308        $field = self::extractField($res, 'ADR'); 
     309        $this->assertEquals($field[1]['adr_stradr'], '1234 rue Marue;bureau 100'); 
     310    } 
     311} 
Note: See TracChangeset for help on using the changeset viewer.