Payment_Process
[ class tree: Payment_Process ] [ index: Payment_Process ] [ all elements ]

Source for file Process.php

Documentation is available at Process.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at                              |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Ian Eure <ieure@php.net>                                    |
  17. // |          Joe Stump <joe@joestump.net>                                |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Process.php,v 1.32 2004/07/01 22:24:16 ieure Exp $
  21.  
  22. require_once 'PEAR.php';
  23. require_once 'Validate.php';
  24. require_once 'Payment/Process/Type.php';
  25.  
  26. // Error codes
  27. define('PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED'-100);
  28. define('PAYMENT_PROCESS_ERROR_NOFIELD'-101);
  29. define('PAYMENT_PROCESS_ERROR_NOPROCESSOR'-102);
  30. define('PAYMENT_PROCESS_ERROR_INCOMPLETE'-1);
  31. define('PAYMENT_PROCESS_ERROR_INVAILD'-2);
  32. define('PAYMENT_PROCESS_ERROR_AVS'-3);
  33. define('PAYMENT_PROCESS_ERROR_CVV'-4);
  34.  
  35. // Transaction actions
  36. // A normal transaction
  37. define('PAYMENT_PROCESS_ACTION_NORMAL'200);
  38. // Authorize only. No funds are transferred.
  39. define('PAYMENT_PROCESS_ACTION_AUTHONLY'201);
  40. // Credit funds back from a previously-charged transaction.
  41. define('PAYMENT_PROCESS_ACTION_CREDIT'202);
  42. // Post-authorize an AUTHONLY transaction.
  43. define('PAYMENT_PROCESS_ACTION_POSTAUTH'203);
  44. // Clear a previous transaction
  45. define('PAYMENT_PROCESS_ACTION_VOID'204);
  46.  
  47. // Transaction sources
  48. define('PAYMENT_PROCESS_SOURCE_POS'300);
  49. define('PAYMENT_PROCESS_SOURCE_ONLINE'301);
  50.  
  51. // Results
  52. define('PAYMENT_PROCESS_RESULT_APPROVED'400);
  53. define('PAYMENT_PROCESS_RESULT_DECLINED'401);
  54. define('PAYMENT_PROCESS_RESULT_OTHER'402);
  55. define('PAYMENT_PROCESS_RESULT_FRAUD'403);
  56. define('PAYMENT_PROCESS_RESULT_DUPLICATE',404);
  57.  
  58. define('PAYMENT_PROCESS_AVS_MATCH'500);
  59. define('PAYMENT_PROCESS_AVS_MISMATCH'501);
  60. define('PAYMENT_PROCESS_AVS_ERROR'502);
  61. define('PAYMENT_PROCESS_AVS_NOAPPLY',503);
  62.  
  63. define('PAYMENT_PROCESS_CVV_MATCH'600);
  64. define('PAYMENT_PROCESS_CVV_MISMATCH'601);
  65. define('PAYMENT_PROCESS_CVV_ERROR'602);
  66. define('PAYMENT_PROCESS_CVV_NOAPPLY'603);
  67.  
  68. /**
  69.  * Payment_Process
  70.  *
  71.  * @author Ian Eure <ieure@php.net>
  72.  * @package Payment_Process
  73.  * @category Payment
  74.  * @version @version@
  75.  */
  76. class Payment_Process extends PEAR {
  77.  
  78.     /**
  79.      * Options.
  80.      *
  81.      * @var array 
  82.      * @see setOptions()
  83.      * @access private;
  84.      */
  85.     var $_options '';
  86.  
  87.     /**
  88.      * Your login name to use for authentication to the online processor.
  89.      *
  90.      * @var string 
  91.      */
  92.     var $login = '';
  93.  
  94.     /**
  95.      * Your password to use for authentication to the online processor.
  96.      *
  97.      * @var string 
  98.      */
  99.     var $password = '';
  100.  
  101.     /**
  102.      * Processing action.
  103.      *
  104.      * This should be set to one of the PAYMENT_PROCESS_ACTION_* constants.
  105.      *
  106.      * @var int 
  107.      */
  108.     var $action = '';
  109.  
  110.     /**
  111.      * A description of the transaction (used by some processors to send
  112.      * information to the client, normally not a required field).
  113.      * @var string 
  114.      */
  115.     var $description = '';
  116.  
  117.     /**
  118.      * The transaction amount.
  119.      *
  120.      * @var double 
  121.      */
  122.     var $amount = 0;
  123.  
  124.     /**
  125.      * An invoice number.
  126.      *
  127.      * @var mixed string or int
  128.      */
  129.     var $invoiceNumber = '';
  130.  
  131.     /**
  132.      * Customer identifier
  133.      *
  134.      * @var mixed string or int
  135.      */
  136.     var $customerId = '';
  137.  
  138.     /**
  139.      * Transaction source.
  140.      *
  141.      * This should be set to one of the PAYMENT_PROCESS_SOURCE_* constants.
  142.      *
  143.      * @var int 
  144.      */
  145.     var $transactionSource;
  146.  
  147.     /**
  148.      * Array of fields which are required.
  149.      *
  150.      * @var array 
  151.      * @access private
  152.      * @see _makeRequired()
  153.      */
  154.     var $_required = array();
  155.     
  156.     /**
  157.      * Processor-specific data.
  158.      *
  159.      * @access private
  160.      * @var array 
  161.      */
  162.     var $_data = array();
  163.  
  164.     /**
  165.      * $_driver
  166.      *
  167.      * @author Joe Stump <joe@joestump.net>
  168.      * @var string $_driver 
  169.      * @access private
  170.      */
  171.     var $_driver = null;
  172.  
  173.     /**
  174.      * Return an instance of a specific processor.
  175.      *
  176.      * @param  string  $type     Name of the processor
  177.      * @param  array   $options  Options for the processor
  178.      * @return mixed Instance of the processor object, or a PEAR_Error object.
  179.      */
  180.     function &factory($type$options = false)
  181.     {
  182.         $class "Payment_Process_".$type;
  183.         if (include_once "Payment/Process/{$type}.php"{
  184.             if (class_exists($class)) {
  185.                 $object new $class($options);
  186.                 $object->_driver = $type;
  187.                 return $object;
  188.             
  189.         }
  190.  
  191.         return PEAR::raiseError('"'.$type.'" processor does not exist',
  192.                                 PAYMENT_PROCESS_ERROR_NOPROCESSOR);
  193.  
  194.     }
  195.  
  196.     /**
  197.      * Set many fields.
  198.      *
  199.      * @param  array  $where  Associative array of data to set, in the format
  200.      *                        'field' => 'value',
  201.      * @return void 
  202.      */
  203.     function setFrom($where)
  204.     {
  205.         foreach ($this->getFields(as $field{
  206.             if (isset($where[$field])) {
  207.                 $this->$field $where[$field];
  208.             }
  209.         }
  210.     }
  211.  
  212.     /**
  213.      * Set a value.
  214.      *
  215.      * This will set a value, such as the credit card number. If the requested
  216.      * field is not part of the basic set of supported fields, it is set in
  217.      * $_options.
  218.      *
  219.      * @param  string  $field  The field to set
  220.      * @param  string  $value  The value to set
  221.      * @return void 
  222.      */
  223.     function set($field$value)
  224.     {
  225.         if (!$this->fieldExists($field)) {
  226.             return PEAR::raiseError("Field \"$field\" does not exist."PAYMENT_PROCESS_ERROR_INVALID);
  227.         }
  228.         $this->$field $value;
  229.         return true;
  230.     }
  231.     
  232.     /**
  233.      * Mark a field (or fields) as being required.
  234.      *
  235.      * @param  string  $field Field name
  236.      * @param  string  ...
  237.      * @return boolean always true.
  238.      */
  239.     function _makeRequired()
  240.     {
  241.         foreach (func_get_args(as $field{
  242.             $this->_required[$field= true;
  243.         }
  244.         return true;
  245.     }
  246.     
  247.     /**
  248.      * Mark a field as being optional.
  249.      *
  250.      * @param  string  $field Field name
  251.      * @param  ... 
  252.      * @return boolean always true.
  253.      */
  254.     function _makeOptional()
  255.     {
  256.         foreach (func_get_args(as $field{
  257.             unset($this->_required[$field]);
  258.         }
  259.         return true;
  260.     }
  261.     
  262.     /**
  263.      * Determine if a field is required.
  264.      *
  265.      * @param  string $field Field to check
  266.      * @return boolean true if required, false if optional.
  267.      */
  268.     function isRequired($field)
  269.     {
  270.         return (isset($this->_required[$field]));
  271.     }
  272.  
  273.     /**
  274.      * Determines if a field exists.
  275.      *
  276.      * @author Ian Eure <ieure@php.net>
  277.      * @param  string  $field  Field to check
  278.      * @return boolean true if field exists, false otherwise
  279.      */
  280.     function fieldExists($field)
  281.     {
  282.         return @in_array($field$this->getFields());
  283.     }
  284.  
  285.     /**
  286.      * Get a list of fields.
  287.      *
  288.      * This function returns an array containing all the possible fields which
  289.      * may be set.
  290.      *
  291.      * @author Ian Eure <ieure@php.net>
  292.      * @access public
  293.      * @return array Array of valid fields.
  294.      */
  295.     function getFields()
  296.     {
  297.         $vars array_keys(get_class_vars(get_class($this)));
  298.         foreach ($vars as $idx => $field{
  299.             if (ereg('^_+'$field)) {
  300.                 unset($vars[$idx]);
  301.             }
  302.         }
  303.         return $vars;
  304.     }
  305.  
  306.     /**
  307.      * Set class options.
  308.      *
  309.      * @author Ian Eure <ieure@php.net>
  310.      * @param  Array  $options         Options to set
  311.      * @param  Array  $defaultOptions  Default options
  312.      * @return void 
  313.      */
  314.     function setOptions($options = false$defaultOptions = false)
  315.     {
  316.         $defaultOptions $defaultOptions $defaultOptions $this->_defaultOptions;
  317.         $this->_options @array_merge($defaultOptions$options);
  318.     }
  319.  
  320.     /**
  321.      * Get an option value.
  322.      *
  323.      * @author Ian Eure <ieure@php.net>
  324.      * @param  string  $option  Option to get
  325.      * @return mixed   Option value
  326.      */
  327.     function getOption($option)
  328.     {
  329.         return @$this->_options[$option];
  330.     }
  331.  
  332.     /**
  333.      * Set an option value
  334.      *
  335.      * @author Joe Stump <joe@joestump.net>
  336.      * @access public
  337.      * @param  string  $option  Option name to set
  338.      * @param  mixed   $value   Value to set
  339.      */
  340.     function setOption($option,$value)
  341.     {
  342.         return ($this->_options[$option$value);
  343.     }
  344.  
  345.     /**
  346.      * See if a value is a defined constant.
  347.      *
  348.      * This function checks to see if $value is defined in one of
  349.      * PAYMENT_PROCESS_{$class}_*. It's used to verify that e.g. $object->action is one of
  350.      * PAYMENT_PROCESS_ACTION_NORMAL, PAYMENT_PROCESS_ACTION_AUTHONLY etc.
  351.      *
  352.      * @access private
  353.      * @param  mixed    $value  Value to check
  354.      * @param  mixed    $class  Constant class to check
  355.      * @return boolean  true if it is defined, false otherwise.
  356.      */
  357.     function _isDefinedConst($value$class)
  358.     {
  359.         $re '^PAYMENT_PROCESS_'.strtoupper($class).'_.*';
  360.         $consts get_defined_constants();
  361.         foreach ($consts as $constant => $constVal{
  362.             if (ereg($re$constant)) {
  363.                 $valid[$constVal;
  364.             }
  365.         }
  366.         return @in_array($value$valid);
  367.     }
  368.  
  369.     /**
  370.      * Statically check a Payment_Result class for success
  371.      *
  372.      * @author Joe Stump <joe@joestump.net>
  373.      * @access public
  374.      * @param  mixed  $obj 
  375.      */
  376.     function isSuccess($obj)
  377.     {
  378.         if (is_a($obj,'Payment_Process_Result')) {
  379.             if ($obj->getCode(== PAYMENT_PROCESS_RESULT_APPROVED{
  380.                 return true;
  381.             }
  382.         }
  383.   
  384.         return false;
  385.     }
  386.  
  387.     /**
  388.     * Statically check a Payment_Result class for error
  389.     *
  390.     * @author Joe Stump <joe@joestump.net>
  391.     * @access public
  392.     * @param  mixed  $obj 
  393.     */
  394.     function isError($obj)
  395.     {
  396.         if (PEAR::isError($obj)) {
  397.             return true; 
  398.         }
  399.  
  400.         if (is_a($obj,'Payment_Process_Result')) {
  401.             if ($obj->getCode(!= PAYMENT_PROCESS_RESULT_APPROVED{
  402.                 return true;
  403.             }
  404.         }
  405.  
  406.         return false;
  407.     
  408. }
  409.  
  410. /**
  411.  * Payment_Process_Result
  412.  *
  413.  * The core result class that should be returned from each driver's process()
  414.  * function. This should be extended as Payment_Process_Result_DriverName and
  415.  * then have the appropriate fields mapped out accordingly.
  416.  *
  417.  * Take special care to appropriately create a parse() function in your result
  418.  * class. You can then call _mapFields() with a resultArray (ie. exploded
  419.  * result) to map your results from parse() into the member variables.
  420.  *
  421.  * Please note that this class keeps your original codes intact so they can
  422.  * be accessed directly and then uses the function wrappers to return uniform
  423.  * Payment_Process codes.
  424.  *
  425.  * @author Joe Stump <joe@joestump.net>
  426.  * @package Payment_Process
  427.  * @category Payment
  428.  * @version @version@
  429.  */
  430.  
  431.     /**
  432.      * Processor instance which this result was instantiated from.
  433.      *
  434.      * This should contain a reference to the requesting Processor.
  435.      *
  436.      * @author Ian Eure <ieure@php.net>
  437.      * @access private
  438.      * @var    Object 
  439.      */
  440.     var $_request;
  441.     
  442.     /**
  443.      * The raw response (ie. from cURL)
  444.      *
  445.      * @author Joe Stump <joe@joestump.net>
  446.      * @access protected
  447.      * @var    string  $_rawResponse 
  448.      */
  449.     var $_rawResponse = null;
  450.  
  451.     /**
  452.      * The approval/decline code
  453.      *
  454.      * The value returned by your gateway as approved/declined should be mapped
  455.      * into this variable. Valid results should then be mapped into the
  456.      * appropriate PAYMENT_PROCESS_RESULT_* code using the $_statusCodeMap
  457.      * array. Values returned into $code should be mapped as keys in the map
  458.      * with PAYMENT_PROCESS_RESULT_* as the values.
  459.      *
  460.      * @author  Joe Stump <joe@joestump.net>
  461.      * @access  public
  462.      * @var     mixed  $code 
  463.      * @see    PAYMENT_PROCESS_RESULT_APPROVED, PAYMENT_PROCESS_RESULT_DECLINED
  464.      * @see    PAYMENT_PROCESS_RESULT_OTHER, $_statusCodeMap
  465.      */
  466.     var $code;
  467.  
  468.     /**
  469.      * Message/Response Code
  470.      *
  471.      * Along with the response (yes/no) you usually get a response/message
  472.      * code that translates into why it was approved/declined. This is where
  473.      * you map that code into. Your $_statusCodeMessages would then be keyed by
  474.      * valid messageCode values.
  475.      *
  476.      * @author  Joe Stump <joe@joestump.net>
  477.      * @access  public
  478.      * @var     mixed  $messageCode 
  479.      * @see     $_statusCodeMessages
  480.      */
  481.     var $messageCode;
  482.  
  483.     /**
  484.      * Message from gateway
  485.      *
  486.      * Map the textual message from the gateway into this variable. It is not
  487.      * currently returned or used (in favor of the $_statusCodeMessages map, but
  488.      * can be accessed directly for debugging purposes.
  489.      *
  490.      * @author  Joe Stump <joe@joestump.net>
  491.      * @access  public
  492.      * @var     string   $message 
  493.      * @see     $_statusCodeMessages
  494.      */
  495.     var $message = 'No message from gateway';
  496.  
  497.     /**
  498.      * Authorization/Approval code
  499.      *
  500.      * @author  Joe Stump <joe@joestump.net>
  501.      * @access  public
  502.      * @var     string  $approvalCode 
  503.      */
  504.     var $approvalCode;
  505.  
  506.     /**
  507.      * Address verification code
  508.      *
  509.      * The AVS code returned from your gateway. This should then be mapped to
  510.      * the appropriate PAYMENT_PROCESS_AVS_* code using $_avsCodeMap. This value
  511.      * should also be mapped to the appropriate textual message via the
  512.      * $_avsCodeMessages array.
  513.      *
  514.      * @author  Joe Stump <joe@joestump.net>
  515.      * @access  public
  516.      * @var     string  $avsCode 
  517.      * @see     PAYMENT_PROCESS_AVS_MISMATCH, PAYMENT_PROCESS_AVS_ERROR
  518.      * @see     PAYMENT_PROCESS_AVS_MATCH, PAYMENT_PROCESS_AVS_NOAPPLY, $_avsCodeMap
  519.      * @see     $_avsCodeMessages
  520.      */
  521.     var $avsCode;
  522.  
  523.     /**
  524.      * Transaction ID
  525.      *
  526.      * This is the unique transaction ID, which is used by gateways to modify
  527.      * transactions (credit, update, etc.). Map the appropriate value into this
  528.      * variable.
  529.      *
  530.      * @author  Joe Stump <joe@joestump.net>
  531.      * @access  public
  532.      * @var     string $transactionId 
  533.      */
  534.     var $transactionId;
  535.  
  536.     /**
  537.      * Invoice Number
  538.      *
  539.      * Unique internal invoiceNumber (ie. your company's order/invoice number
  540.      * that you assign each order as it is processed). It is always a good idea
  541.      * to pass this to the gateway (which is usually then echo'd back).
  542.      *
  543.      * @author Joe Stump <joe@joestump.net>
  544.      * @access public
  545.      * @var string $invoiceNumber 
  546.      */
  547.     var $invoiceNumber;
  548.  
  549.     /**
  550.     * Customer ID
  551.     *
  552.     * Unique internall customer ID (ie. your company's customer ID used to
  553.     * track individual customers).
  554.     *
  555.     * @author Joe Stump <joe@joestump.net>
  556.     * @access public
  557.     * @var string $customerId 
  558.     */
  559.     var $customerId;
  560.  
  561.     /**
  562.     * CVV Code
  563.     *
  564.     * The CVV code is the 3-4 digit number on the back of most credit cards.
  565.     * This value should be mapped via the $_cvvCodeMap variable to the
  566.     * appropriate PAYMENT_PROCESS_CVV_* values.
  567.     *
  568.     * @author Joe Stump <joe@joestump.net>
  569.     * @access public
  570.     * @var string $cvvCode 
  571.     */
  572.     var $cvvCode = PAYMENT_PROCESS_CVV_NOAPPLY;
  573.  
  574.     /**
  575.     * CVV Message
  576.     *
  577.     * Your cvvCode value should be mapped to appropriate messages via the
  578.     * $_cvvCodeMessage array. This value is merely here to hold the value
  579.     * returned from the gateway (if any).
  580.     *
  581.     * @author Joe Stump <joe@joestump.net>
  582.     * @access public
  583.     * @var string $cvvMessage 
  584.     */
  585.     var $cvvMessage = 'No CVV message from gateway';
  586.  
  587.     function Payment_Process_Result($rawResponse
  588.     {
  589.         $this->_rawResponse = $rawResponse;
  590.     }
  591.  
  592.     function &factory($type,$rawResponse)
  593.     {
  594.         $class 'Payment_Process_Result_'.$type;
  595.         if (class_exists($class)) {
  596.             return new $class($rawResponse);
  597.         }
  598.  
  599.         return PEAR::raiseError('Invalid response type: '.$type.'('.$class.')');
  600.     }
  601.  
  602.     // {{{ validate()
  603.     /**
  604.     * validate
  605.     * 
  606.     * @author Joe Stump <joe@joestump.net>
  607.     * @access public
  608.     * @return mixed 
  609.     */
  610.     function validate()
  611.     {
  612.         if ($this->_request->getOption('avsCheck'=== true{
  613.             if ($this->getAVSCode(!= PAYMENT_PROCESS_AVS_MATCH{
  614.                 return PEAR::raiseError('AVS check failed',
  615.                                         PAYMENT_PROCESS_ERROR_AVS);
  616.             }
  617.         }    
  618.  
  619.         $paymentType $this->_request->_payment->_type;
  620.         if ($this->_request->getOption('cvvCheck'=== true &&
  621.             $paymentType == PAYMENT_PROCESS_TYPE_CREDITCARD{
  622.  
  623.             if ($this->getCvvCode(!= PAYMENT_PROCESS_CVV_MATCH{
  624.                 return PEAR::raiseError('CVV check failed',
  625.                                         PAYMENT_PROCESS_ERROR_CVV);
  626.             }
  627.  
  628.         }
  629.  
  630.         if ($this->getCode(!= PAYMENT_PROCESS_RESULT_APPROVED{
  631.             return PEAR::raiseError($this->getMessage(),
  632.                                     PAYMENT_PROCESS_RESULT_DECLINED)
  633.         
  634.  
  635.         return true;
  636.     }
  637.     // }}}
  638.  
  639.     // {{{ parse()
  640.     /**
  641.     * parse
  642.     *
  643.     * @abstract
  644.     * @author Joe Stump <joe@joestump.net>
  645.     * @access public
  646.     */
  647.     function parse(
  648.     {
  649.         return PEAR::raiseError('parse() not implemented',
  650.                                 PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED);
  651.     }
  652.     // }}}
  653.  
  654.     // {{{ getCode()
  655.     /**
  656.     * getCode
  657.     *  
  658.     * @author Joe Stump <joe@joestump.net>
  659.     * @access public
  660.     */
  661.     function getCode(
  662.     {
  663.         if (isset($this->_statusCodeMap[$this->code])) {
  664.             return $this->_statusCodeMap[$this->code];
  665.         else {
  666.             return PAYMENT_PROCESS_RESULT_DECLINED;
  667.         }
  668.     }
  669.     // }}}
  670.  
  671.     // {{{ getMessage()
  672.     /**
  673.     * getMessage
  674.     *
  675.     * Return the message from the code map, or return the raw message if
  676.     * there is one. Otherwise, return a worthless message.
  677.     *
  678.     * @author Joe Stump <joe@joestump.net>
  679.     * @access public
  680.     * @return string 
  681.     */
  682.     function getMessage(
  683.     {
  684.         if (isset($this->_statusCodeMessages[$this->messageCode])) {
  685.             return $this->_statusCodeMessages[$this->messageCode];
  686.         elseif(strlen($this->message)) {
  687.             return $this->message
  688.         else {
  689.             return 'No message reported';
  690.         }
  691.     }
  692.     // }}} 
  693.  
  694.     function getAVSCode(
  695.     {
  696.         return $this->_avsCodeMap[$this->avsCode];
  697.     }
  698.  
  699.     function getAVSMessage(
  700.     {
  701.         return $this->_avsCodeMessages[$this->avsCode];
  702.     }
  703.  
  704.     function getCvvCode()
  705.     {
  706.         return $this->_cvvCodeMap[$this->cvvCode];
  707.     }
  708.  
  709.     function getCvvMessage()
  710.     {
  711.         return $this->_cvvCodeMessages[$this->cvvCode];
  712.     }
  713.  
  714.     // {{{ _mapFields()
  715.     /**
  716.     * _mapFields
  717.     *
  718.     * @author Joe Stump <joe@joestump.net>
  719.     * @access private
  720.     * @param mixed $responseArray 
  721.     */
  722.     function _mapFields($responseArray{
  723.         foreach($this->_fieldMap as $key => $val{
  724.             $this->$val $responseArray[$key]
  725.         }
  726.     }
  727.     // }}}
  728. }
  729.  
  730. ?>

Documentation generated on Mon, 11 Mar 2019 13:58:49 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.