Location: PHPKode > projects > V-webmail > includes/vCard.php
<?php
/**
* Filename.......: vCard.php
* Project........: V-webmail
* Last Modified..: $Date: 2006/01/25 00:04:47 $
* CVS Revision...: $Revision: 1.2 $
* Copyright......: 2001-2004 Richard Heyes
*
* Based on vCard PHP <http://vcardphp.sourceforge.net>
*/

class vCard
{
    /**
    * An associative array where each key is the property name and each value
    * is a VCardProperty array of properties which share that property name.
    */
    var $_map;

	/**
    * The raw vCard data as an array of lines
	* @var arrray
    */
	var $_data;

	/**
    * Shortcut attributes object
	* @var object
    */
	var $attributes;

	/**
    * Constructor
	*
	* @param string $data The raw vCard text
    */
	function vCard($data = '')
	{
		$this->attributes = new stdClass;

		// Unfold any folded lines
		$this->_data = preg_replace('/\r?\n( |\t)/', '', $data);
		$this->_data = preg_split("/\r?\n/", $this->_data, -1, PREG_SPLIT_NO_EMPTY);
		$this->_parse();
	}

    /**
    * Parses a vCard from one or more lines. Lines that are not property
    * lines, such as blank lines, are skipped. Returns false if there are
    * no more lines to be parsed.
    *
	* @param string $lines The raw vCard text
    */
    function _parse()
    {
		foreach ($this->_data as $line) {
			$tmp = $this->_split(':', $line, 2);
			
			if (count($tmp) == 2) {
                $params = $this->_split(';', strtoupper($tmp[0]));
                $name   = $params[0];

				// Not interested in these
				if ($name == 'BEGIN' OR $name == 'END') {
					continue;
				}

				$this->attributes->{$name}[] = new stdClass;
				$currAttribute = &$this->attributes->{$name}[count($this->attributes->{$name}) - 1];

				$currAttribute->value = preg_replace('/(?<!\\\\)\\\\(,|;)/', '\1', $tmp[1]);
				$currAttribute->parameters = array();

				array_shift($params);

				foreach ($params as $param) {
					$this->_parseParam($currAttribute, $param);
				}

               if (@$currAttribute->parameters['ENCODING'][0] == 'QUOTED-PRINTABLE') {
                   $this->_decodeQuotedPrintable($currAttribute, $this->_data);
               }

               if (@$currAttribute->parameters['CHARSET'][0] == 'UTF-8') {
                   $currAttribute->value = utf8_decode($this->value);
               }
			}
		}
    }

    /**
    * Parses a parameter string where the parameter string is either in the
    * form "name=value[,value...]" such as "TYPE=WORK,CELL" or is a
    * vCard 2.1 parameter value such as "WORK" in which case the parameter
    * name is determined from the parameter value.
	*
	* @param string $currAttribute The attribute to add the params to
	* @param string $param         The parameter to parse
    */
    function _parseParam(&$currAttribute, $param)
    {
        $tmp = $this->_split('=', $param, 2);

        if (count($tmp) == 1) {
            $value = $tmp[0]; 
            $name = $this->_getParamNameFromValue($value);
            $currAttribute->parameters[$name][] = $value;
        } else {
            $name   = $tmp[0];
            $values = $this->_split(',', $tmp[1]);
            foreach ($values as $value) {
                $currAttribute->parameters[$name][] = $value;
            }
        }
    }

    /**
    * The vCard 2.1 specification allows parameter values without a name.
    * The parameter name is then determined from the unique parameter value.
	*
	* @param  string $value The value of the parameter
	* @return string        The name of the parameter
    */
    function _getParamNameFromValue($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');
        static $values    = array ('INLINE', 'URL', 'CID');
        static $encodings = array ('7BIT', 'QUOTED-PRINTABLE', 'BASE64');

        if (in_array($value, $types)) {
            return 'TYPE';

        } elseif (in_array($value, $values)) {
            return 'VALUE';

        } elseif (in_array($value, $encodings)) {
            return 'ENCODING';
        }

        return 'UNKNOWN';
    }

    /**
    * Decodes a quoted printable value spanning multiple lines.
	*
	* @param object &$currAttribute The attribute currently being parsed
	* @param array   $lines         The lines of the vCard
    */
    function _decodeQuotedPrintable(&$currAttribute, $lines)
    {
        $value = &$currAttribute->value;

        while ($value[strlen($value) - 1] == "=") {
            $value = substr($value, 0, strlen($value) - 1);
            if (!(list(, $line) = each($lines))) {
                break;
            }
            $value .= rtrim($line);
        }
        $value = quoted_printable_decode($value);
    }
	
	/**
    * Splits a string based on a delimiter but only
	* said delimiter is not quoted.
	*
	* @param  string  $delimiter The delimiter to split on
	* @param  string  $input     The input string
	* @param  integer $maxParts  Max number of parts to return
	* @return array              The split parts
    */
	function _split($delimiter, $input, $maxParts = 0)
	{
	    $quote  = false;
	    $length = strlen($input);

	    for ($i = 0; $i < $length AND ($maxParts == 0 OR $maxParts > 1); $i++) {
	        $char = $input{$i};
	        if ($char == '"') {
	            $quote = !$quote;
	        } elseif (!$quote AND $char == $delimiter) {
	            $input{$i} = "\x00";
	            if ($maxParts > 0) {
	                $maxParts--;
	            }
	        }
	    }
	    return explode("\x00", $input);
	}
	
	/**
    * Generic accessor to add an attribute
	*
	* @param string $name   Name of attribute
	* @param string $value  Value of attribute
	* @param array  $params Any parameters
    */
	function addAttribute($name, $value, $params = array())
	{
		$obj = new stdClass;
		$obj->value = $value;
		$obj->parameters = $params;

		$this->attributes->{$name}[] = $obj;
	}
	
	function clearAttribute($name)
	{
		if (!empty($this->attributes->$name)) {
			unset($this->attributes->$name);
		}
	}

	/**
    * Accessors
    */
	function addName($surname, $forename, $additional = '', $prefix = '', $suffix = '')
	{
		$value = sprintf('%s;%s;%s;%s;%s', $surname, $forename, $additional, $prefix, $suffix);
		$this->addAttribute('N', $value);
	}
	
	function addFormattedName($value)
	{
		$this->addAttribute('FN', $value);
	}
	
	function addNickname($value)
	{
		$this->addAttribute('NICKNAME', $value);
	}
	
	function addTelephone($value, $types = array())
	{
		$params = array();

		if (!empty($types)) {
			$params['TYPE'] = $types;
		}
		
		$this->addAttribute('TEL', $value, $params);
	}
	
	function addEmail($value, $types = array())
	{
		$params = array();

		if (!empty($types)) {
			$params['TYPE'] = $types;
		}
		
		$this->addAttribute('EMAIL', $value, $params);
	}
	
	function addAddress($street, $city, $province, $postcode, $country, $pobox = '', $extended = '')
	{
		$value = sprintf('%s;%s;%s;%s;%s;%s;%s', $pobox, $extended, $street, $city, $province, $postcode, $country);
		$this->addAttribute('ADR', $value);
	}
	
	function addNote($value)
	{
		$params = array();
		if (strpos($value, "\r\n") !== false) {
			// QP encode the note, though this technically breaks rfc2426
			$params['ENCODING'][] = 'QUOTED-PRINTABLE';
		}

		$this->addAttribute('NOTE', $value, $params);
	}
	
	function addCategories($value)
	{
		$value = implode(',', $value);
		$this->addAttribute('CATEGORIES', $value);
	}
	
	/**
    * Returns number of instances of a given attribute
	*
	* @param  string  $name Name of the attribute
	* @return integer       Number of instances
    */
	function numInstances($name)
	{
		if (!empty($this->attributes->$name)) {
			return count($this->attributes->$name);
		}
		
		return 0;
	}

    /**
    * Returns the first instance of the specified attribute name or null if
    * there are no attributes with that name.
	*
	* @param  string $name Name of the attribute
	* @return object       The attribute
    */
    function getFirstInstanceOf($name)
    {
        if (!empty($this->attributes->{$name}[0])) {
			return $this->attributes->{$name}[0];
		}
		
		return null;
    }

    /**
    * Returns the all instances of the specified attribute name or null if
    * there are no attributes with that name.
	*
	* @param  string $name Name of the attribute
	* @return array        The attribute(s)
    */
    function getAllInstancesOf($name)
    {
        if (!empty($this->attributes->{$name})) {
			return $this->attributes->{$name};
		}

		return null;
    }

    /**
    * Returns an array of the categories this vCard is
	* assigned to.
	*
	* @return array The group list
    */
    function getCategories()
    {
		$result = array();

        $property = $this->getFirstInstanceOf('CATEGORIES');

        if ($property) {
            $result = $this->_split(',', $property->value);
        }

        return $result;
    }

    /**
    * Returns true if the card belongs to the supplied category.
	* 
	* @param  string $category The category to check for
	* @return bool             Whether this vCard is in the category or not
    */
    function inCategory($category)
    {
        $vcCategories = $this->getCategories();
        foreach ($categories as $category) {
            if (in_array($category, $vcCategories)) {
                return true;
            }
        }

        return false;
    }

    /**
    * Splits the value on unescaped delimiter characters.
	*
	* @param  string $input     The input string
	* @param  string $delimiter The delimiter to split on
	* @return array             The split values
    */
    function getValues($input, $delimiter = ';')
    {
		// Split on unescaped delimiters
		$values = preg_split('/(?<!\\\\)' . preg_quote($delimiter) . '/', $input);

		// Unescape escaped delimiters
		for ($i=0; $i<count($values); $i++) {
			$values[$i] = str_replace('\\' . $delimiter, $delimiter, $values[$i]);
		}
		
		return $values;
    }
	
	/**
    * Generates a vCard from the current set
	* of attributes
	*
	* @return string The vCard text
    */
	function generateVcard()
	{
		$lines = array();
		$lines[] = 'BEGIN:VCARD';
		$lines[] = 'VERSION:3.0';

		foreach ($this->attributes as $attName => $attObjs) {
			foreach ($attObjs as $attObj) {
				if (!empty($attObj->parameters['ENCODING']) AND $attObj->parameters['ENCODING'][0] == 'QUOTED-PRINTABLE') {
					$value = preg_replace('/([^\x20\x21-\x3C\x3E-\x7E])/e', 'sprintf("=%02X", ord("\1"))', $attObj->value);
				} else {
					$value = $attObj->value;
				}

				$params = array();
				foreach ($attObj->parameters as $paramName => $paramValues) {
					$params[] = sprintf('%s=%s', $paramName, implode(',', $paramValues));
				}
				
				/**
                * Escape certain values
                */
				if (in($attName, 'NOTE')) {
					$value = strtr($value, array(';' => '\\;', ',' => '\\,'));
				}

				$lines[] = sprintf('%s%s:%s', $attName, (!empty($params) ? ';' . implode(';', $params) : ''), $value);
			}
		}

		$lines[] = 'END:VCARD';

		return implode("\r\n", $lines);
	}
}
?>
Return current item: V-webmail