Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: b9b525a8ef
Fetching contributors…

Cannot retrieve contributors at this time

644 lines (554 sloc) 20.028 kB
<?php
/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
/**+----------------------------------------------------------------------+
* | PHP version 5 |
* +----------------------------------------------------------------------+
* | Copyright (c) 1997-2008 The PHP Group |
* +----------------------------------------------------------------------+
* | All rights reserved. |
* | |
* | Redistribution and use in source and binary forms, with or without |
* | modification, are permitted provided that the following conditions |
* | are met: |
* | |
* | - Redistributions of source code must retain the above copyright |
* | notice, this list of conditions and the following disclaimer. |
* | - Redistributions in binary form must reproduce the above copyright |
* | notice, this list of conditions and the following disclaimer in the |
* | documentation and/or other materials provided with the distribution. |
* | - Neither the name of the The PEAR Group nor the names of its |
* | contributors may be used to endorse or promote products derived from |
* | this software without specific prior written permission. |
* | |
* | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
* | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
* | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
* | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
* | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
* | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
* | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
* | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
* | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
* | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
* | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
* | POSSIBILITY OF SUCH DAMAGE. |
* +----------------------------------------------------------------------+
*
* @category File_Formats
* @package File_IMC
* @author Paul M. Jones <pmjones@ciaweb.net>
* @license http://www.opensource.org/licenses/bsd-license.php The BSD License
* @version CVS: $Id$
* @link http://pear.php.net/package/File_IMC
*/
/**
*
* This class helps build files in the vCard and vCalendar formats.
*
* General note: we use the terms "set" "add" and "get" as function
* prefixes.
*
* "Set" means there is only one iteration of a component, and it has
* only one value repetition, so you set the whole thing at once.
*
* "Add" means either multiple iterations of a component are allowed, or
* that there is only one iteration allowed but there can be multiple
* value repetitions, so you add iterations or repetitions to the current
* stack.
*
* "Get" returns the full vCard line for a single iteration.
*
* @author Paul M. Jones <pmjones@ciaweb.net>
*
* @package File_IMC
*
*/
class File_IMC_Build
{
/**
*
* Values for vCard components.
*
* @access public
*
* @var array
*
*/
var $value = array();
/**
*
* Parameters for vCard components.
*
* @access public
*
* @var array
*
*/
var $param = array();
/**
*
* Tracks which component (N, ADR, TEL, etc) value was last set or added.
* Used when adding parameters automatically to the proper component.
*
* @var string
*/
var $autoparam = null;
/**
*
* 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
*
* @access public
*
*/
function reset($version = null)
{
$prev = $this->value['VERSION'][0][0][0];
$this->value = array();
$this->param = array();
$this->autoparam = null;
if ($version === null) {
$this->setVersion($prev);
} else {
$this->setVersion($version);
}
}
/**
*
* 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.
*
* @access public
*
* @abstract
*
*/
function setVersion($text)
{
// overload in driver to check for valid versions
}
/**
*
* Gets back the version of the the vCard. Only one iteration.
*
* @return string The data-source of the vCard.
*
* @access public
*
*/
function getVersion()
{
return $this->getMeta('VERSION', 0) .
$this->getValue('VERSION', 0);
}
/**
*
* 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 mixed Void on success
* @throws File_IMC_Exception on failure.
*
*
* @access 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);
} elseif (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;
}
} else {
/*
// escape colons not led by a backslash
$regex = '(?<!\\\\)(\:)';
$text = preg_replace("/$regex/i", "\\:", $text);
// escape semicolons not led by a backslash
$regex = '(?<!\\\\)(\;)';
$text = preg_replace("/$regex/i", "\\;", $text);
// escape commas not led by a backslash
$regex = '(?<!\\\\)(\,)';
$text = preg_replace("/$regex/i", "\\,", $text);
// escape newlines
$regex = '\\n';
$text = preg_replace("/$regex/i", "\\n", $text);
*/
// combined (per note from Daniel Convissor)
$regex = '(?<!\\\\)([\:\;\,\\n])';
$text = preg_replace("/$regex/", '\\\$1', $text);
}
}
/**
*
* Adds a parameter value for a given component and parameter name.
*
* Note that although vCard 2.1 and vCalendar allow you to specify a
* parameter value without a name (e.g., "HOME" instead of
* "TYPE=HOME") this class is not so lenient. ;-) You must
* specify a parameter name (TYPE, ENCODING, etc) when adding a
* parameter. Call multiple times if you want to add multiple values
* to the same parameter. E.g.:
*
* $vcard = File_IMC::build('vCard');
*
* // set "TYPE=HOME,PREF" for the first TEL component
* $vcard->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 $comp The vCard component for which this is a
* paramter (ADR, TEL, etc). If null, will be the component that was
* last set or added-to.
*
* @param mixed $iter An integer vCard component iteration that this
* is a param for. E.g., if you have more than one ADR component, 0
* refers to the first ADR, 1 to the second ADR, and so on. If null,
* the parameter will be added to the last component iteration
* available.
*
* @return mixed Void on success
* @throws File_IMC_Excpetion on failure.
*
* @access public
*
*/
function addParam($param_name, $param_value, $comp = null,
$iter = null)
{
// if component is not specified, default to the last component
// that was set or added.
if ($comp === null) {
$comp = $this->autoparam;
}
// using a null iteration means the param should be associated
// with the latest/most-recent iteration of the selected
// component.
if ($iter === null) {
$iter = count($this->value[$comp]) - 1;
}
// massage the text arguments
$comp = strtoupper(trim($comp));
$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 $comp; must be a positive integer.",
FILE_IMC::ERROR_INVALID_ITERATION);
}
try {
$result = $this->validateParam($param_name, $param_value, $comp, $iter);
} catch (File_IMC_Exception $e) {
throw $e; // FIXME: check later
}
$this->param[$comp][$iter][$param_name][] = $param_value;
}
/**
*
* 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 $comp Optional, the component name (e.g., ADR or
* PHOTO). Only used for error messaging.
*
* @param string $iter Optional, the iteration of the component. Only
* used for error messaging.
*
* @return mixed Boolean true if the parameter is valid
* @throws File_IMC_Exception if not.
*
* @access public
*
* @abstract
*
*/
function validateParam($name, $text, $comp = null, $iter = null)
{
// this must be overloaded in the drivers! by default, nothing
// is valid (this is a reminder to at least write a stub method
// in your driver ;-).
return false;
}
/**
*
* Gets the left-side/prefix/before-the-colon (metadata) part of a
* vCard line, including the component identifier, the parameter
* list, and a colon.
*
* @param string $comp The component to get metadata for (ADR, TEL,
* etc).
*
* @param int $iter The vCard component iteration to get the metadata
* for. E.g., if you have more than one ADR component, 0 refers to
* the first ADR, 1 to the second ADR, and so on.
*
* @return string The line prefix metadata.
*
* @access public
*
*/
function getMeta($comp, $iter = 0)
{
$params = $this->getParam($comp, $iter);
if (trim($params) == '') {
// no parameters
$text = $comp . ':';
} else {
// has parameters. put an extra semicolon in.
$text = $comp . ';' . $params . ':';
}
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 $comp The component to set the value for ('N',
* 'ADR', etc).
*
* @param int $iter The component-iteration to set the value for.
*
* @param int $part The part number of the component-iteration to set
* the value for.
*
* @param mixed $text A string or array; the set of repeated values
* for this component-iteration part.
*
* @return void
*
* @access public
*
*/
function setValue($comp, $iter, $part, $text)
{
$comp = strtoupper($comp);
settype($text, 'array');
$this->value[$comp][$iter][$part] = $text;
$this->autoparam = $comp;
}
/**
*
* 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 $comp The component to set the value for ('N',
* 'ADR', etc).
*
* @param int $iter The component-iteration to set the value for.
*
* @param int $part The part number of the component-iteration to set
* the value for.
*
* @param mixed $text A string or array; the set of repeated values
* for this component-iteration part.
*
* @return void
*
* @access public
*
*/
function addValue($comp, $iter, $part, $text)
{
$comp = strtoupper($comp);
settype($text, 'array');
foreach ($text as $val) {
$this->value[$comp][$iter][$part][] = $val;
}
$this->autoparam = $comp;
}
/**
*
* Generic, all-purpose method to get back the data stored in $this->value.
*
* @param string $comp The component to set the value for ('N',
* 'ADR', etc).
*
* @param int $iter The component-iteration to set the value for.
*
* @param int $part The part number of the component-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 component-iteration part (or specific
* repetition within the part).
*
* @access public
*
*/
function getValue($comp, $iter = 0, $part = 0, $rept = null)
{
if ($rept === null &&
is_array($this->value[$comp][$iter][$part]) ) {
// get all repetitions of a part
$list = array();
foreach ($this->value[$comp][$iter][$part] as $key => $val) {
$list[] = trim($val);
}
$this->escape($list);
return implode(',', $list);
} else {
// get a specific repetition of a part
$text = trim($this->value[$comp][$iter][$part][$rept]);
$this->escape($text);
return $text;
}
}
/**
*
* Gets back the parameter string for a given component.
*
* @param string $comp The component to get parameters for (ADR, TEL,
* etc).
*
* @param int $iter The vCard component iteration to get the param
* list for. E.g., if you have more than one ADR component, 0 refers
* to the first ADR, 1 to the second ADR, and so on.
*
* @return string
*
* @access public
*
*/
function getParam($comp, $iter = 0)
{
$comp = strtoupper($comp);
$text = '';
if (is_array($this->param[$comp][$iter])) {
// loop through the array of parameters for
// the component
foreach ($this->param[$comp][$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()
*
* @access public
*
*/
function setFromArray($src)
{
// reset to a blank values and params
$this->value = array();
$this->param = array();
foreach ($src as $card => $card_val) {
// loop through components (N, ADR, TEL, etc)
foreach ($card_val AS $comp => $comp_val) {
// set the autoparam property. not really needed, but let's
// behave after an expected fashion, shall we? ;-)
$this->autoparam = $comp;
// iteration number of each component
foreach ($comp_val AS $iter => $iter_val) {
// value or param?
foreach ($iter_val AS $kind => $kind_val) {
// part number
foreach ($kind_val AS $part => $part_val) {
// repetition number and text value
foreach ($part_val AS $rept => $text) {
// ignore data when $kind is neither 'value'
// nor 'param'
if (strtolower($kind) == 'value') {
$this->value[strtoupper($comp)][$iter][$part][$rept] = $text;
} elseif (strtolower($kind) == 'param') {
$this->param[strtoupper($comp)][$iter][$part][$rept] = $text;
}
}
}
}
}
}
}
}
/**
*
* Fetches a full vCard/vCal text block based on $this->value and
* $this->param.
*
* @return string A properly formatted vCard/vCalendar text block.
*
* @access public
*
*/
function fetch()
{
// this method must be overloaded in a driver!
}
}
?>
Jump to Line
Something went wrong with that request. Please try again.