You are here

class CMBase in Campaign Monitor 5.2

Same name and namespace in other branches
  1. 6.3 lib/CMBase.php \CMBase
  2. 6.2 lib/CMBase.php \CMBase

Hierarchy

Expanded class hierarchy of CMBase

File

lib/CMBase.php, line 66

View source
class CMBase {
  var $api = '', $campaign_id = 0, $client_id = 0, $list_id = 0;
  var $method = 'get', $url = '', $soapAction = '', $curl = true, $curlExists = true;

  // debugging options
  var $debug_level = 0, $debug_request = '', $debug_response = '', $debug_url = '', $debug_info = array(), $show_response_headers = 0;

  /**
   * @param string $api Your API key.
   * @param string $client The default ClientId you're going to work with.
   * @param string $campaign The default CampaignId you're going to work with.
   * @param string $list The default ListId you're going to work with.
   * @param string $method Determines request type. Values are either get, post, or soap.
   */
  function CMBase($api = null, $client = null, $campaign = null, $list = null, $method = 'get') {
    $this->api = $api;
    $this->client_id = $client;
    $this->campaign_id = $campaign;
    $this->list_id = $list;
    $this->method = $method;
    $this->curlExists = function_exists('curl_init') && function_exists('curl_setopt');
  }

  /**
   * The direct way to make an API call. This allows developers to include new API
   * methods that might not yet have a wrapper method as part of the package.
   *
   * @param string $action The API call.
   * @param array $options An associative array of values to send as part of the request.
   * @return array The parsed XML of the request.
   */
  function makeCall($action = '', $options = array()) {

    // NEW [2008-06-24]: switch to soap automatically for these calls
    $old_method = $this->method;
    if ($action == 'Subscriber.AddWithCustomFields' || $action == 'Subscriber.AddAndResubscribeWithCustomFields' || $action == 'Campaign.Create') {
      $this->method = 'soap';
    }
    if (!$action) {
      return null;
    }
    $url = $this->url;

    // DONE: like facebook's client, allow for get/post through the file wrappers
    // if curl isn't available. (or maybe have curl-emulating functions defined
    // at the bottom of this script.)

    //$ch = curl_init();
    if (!isset($options['header'])) {
      $options['header'] = array();
    }
    $options['header'][] = 'User-Agent: CMBase URL Handler 1.5';
    $postdata = '';
    $method = 'GET';
    if ($this->method == 'soap') {
      $options['header'][] = 'Content-Type: text/xml; charset=utf-8';
      $options['header'][] = 'SOAPAction: "' . $this->soapAction . $action . '"';
      $postdata = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
      $postdata .= "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
      $postdata .= " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
      $postdata .= " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n";
      $postdata .= "<soap:Body>\n";
      $postdata .= "\t<{$action} xmlns=\"{$this->soapAction}\">\n";
      $postdata .= "\t\t<ApiKey>{$this->api}</ApiKey>\n";
      if (isset($options['params'])) {
        $postdata .= array2xml($options['params'], "\t\t");
      }
      $postdata .= "\t</{$action}>\n";
      $postdata .= "</soap:Body>\n";
      $postdata .= "</soap:Envelope>";
      $method = 'POST';

      //curl_setopt( $ch, CURLOPT_POST, 1 );

      //curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
    }
    else {
      $postdata = "ApiKey={$this->api}";
      $url .= "/{$action}";

      // NOTE: since this is GET, the assumption is that params is a set of simple key-value pairs.
      if (isset($options['params'])) {
        foreach ($options['params'] as $k => $v) {
          $postdata .= '&' . $k . '=' . rawurlencode(utf8_encode($v));
        }
      }
      if ($this->method == 'get') {
        $url .= '?' . $postdata;
        $postdata = '';
      }
      else {
        $options['header'][] = 'Content-Type: application/x-www-form-urlencoded';
        $method = 'POST';

        //curl_setopt( $ch, CURLOPT_POST, 1 );

        //curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
      }
    }
    $res = '';

    // WARNING: using fopen() does not recognize stream contexts in PHP 4.x, so
    // my guess is using fopen() in PHP 4.x implies that POST is not supported
    // (otherwise, how do you tell fopen() to use POST?). tried fsockopen(), but
    // response time was terrible. if someone has more experience with working
    // directly with streams, please troubleshoot that.
    // NOTE: fsockopen() needs a small timeout to force the socket to close.
    // it's defined in SOCKET_TIMEOUT.
    // preferred method is curl, only if it exists and $this->curl is true.
    if ($this->curl && $this->curlExists) {
      $ch = curl_init();
      if ($this->method != 'get') {
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
      }
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_HTTPHEADER, $options['header']);
      curl_setopt($ch, CURLOPT_HEADER, $this->show_response_headers);

      // except for the response, all other information will be stored when debugging is on.
      $res = curl_exec($ch);
      if ($this->debug_level) {
        $this->debug_url = $url;
        $this->debug_request = $postdata;
        $this->debug_info = curl_getinfo($ch);
        $this->debug_info['headers_sent'] = $options['header'];
      }
      $this->debug_response = $res;
      curl_close($ch);
    }
    else {

      // 'header' is actually the entire HTTP payload. as such, you need
      // Content-Length header, otherwise you'll get errors returned/emitted.
      $postLen = strlen($postdata);
      $ctx = array(
        'method' => $method,
        'header' => implode("\n", $options['header']) . "\nContent-Length: " . $postLen . "\n\n" . $postdata,
      );
      if ($this->debug_level) {
        $this->debug_url = $url;
        $this->debug_request = $postdata;
        $this->debug_info['overview'] = 'Used stream_context_create()/fopen() to make request. Content length=' . $postLen;
        $this->debug_info['headers_sent'] = $options['header'];

        //$this->debug_info['complete_content'] = $ctx;
      }
      $pv = PHPVER;

      // the preferred non-cURL way if user is using PHP 5.x
      if ($pv[0] == '5') {
        $context = stream_context_create(array(
          'http' => $ctx,
        ));
        $fp = fopen($url, 'r', false, $context);
        ob_start();
        fpassthru($fp);
        fclose($fp);
        $res = ob_get_clean();
      }
      else {

        // this should work with PHP 4, but it seems to take forever to get data back this way
        // NOTE: setting the default_socket_timeout seems to alleviate this issue [finally].
        list($protocol, $url) = explode('//', $url, 2);
        list($domain, $path) = explode('/', $url, 2);
        $fp = fsockopen($domain, 80, $tvar, $tvar2, SOCKET_TIMEOUT);
        if ($fp) {
          $payload = "{$method} /{$path} HTTP/1.1\n" . "Host: {$domain}\n" . $ctx['header'];
          fwrite($fp, $payload);

          // even with the socket timeout set, using fgets() isn't playing nice, but
          // fpassthru() seems to be doing the right thing.
          ob_start();
          fpassthru($fp);
          list($headers, $res) = explode("\r\n\r\n", ob_get_clean(), 2);
          if ($this->debug_level) {
            $this->debug_info['headers_received'] = $headers;
          }
          fclose($fp);
        }
        elseif ($this->debug_level) {
          $this->debug_info['overview'] .= "\nOpening {$domain}/{$path} failed!";
        }
      }
    }

    //$this->method = $old_method;
    if ($res) {
      if ($this->method == 'soap') {
        $tmp = xml2array($res, '/soap:Envelope/soap:Body');
        if (!is_array($tmp)) {
          return $tmp;
        }
        else {
          return $tmp[$action . 'Response'][$action . 'Result'];
        }
      }
      else {
        return xml2array($res);
      }
    }
    else {
      return null;
    }
  }

  /**
   * Wrapper for Subscribers.GetActive. This method triples as Subscribers.GetUnsubscribed
   * and Subscribers.GetBounced when the very last parameter is overridden.
   *
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param string $action (Optional) Set the actual API method to call. Defaults to Subscribers.GeActive if no other valid value is given.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetActive.aspx
   */
  function subscribersGetActive($date = 0, $list_id = null, $action = 'Subscribers.GetActive') {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    if (is_numeric($date)) {
      $date = date('Y-m-d H:i:s', $date);
    }
    $valid_actions = array(
      'Subscribers.GetActive' => '',
      'Subscribers.GetUnsubscribed' => '',
      'Subscribers.GetBounced' => '',
    );
    if (!isset($valid_actions[$action])) {
      $action = 'Subscribers.GetActive';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Date' => $date,
      ),
    ));
  }

  /**
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetUnsubscribed.aspx
   */
  function subscribersGetUnsubscribed($date = 0, $list_id = null) {
    return $this
      ->subscribersGetActive($date, $list_id, 'Subscribers.GetUnsubscribed');
  }

  /**
   * @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetBounced.aspx
   */
  function subscribersGetBounced($date = 0, $list_id = null) {
    return $this
      ->subscribersGetActive($date, $list_id, 'Subscribers.GetBounced');
  }

  /**
   * subscriberAdd()
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
   * @see http://www.campaignmonitor.com/api/Subscriber.Add.aspx
   */
  function subscriberAdd($email, $name, $list_id = null, $resubscribe = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.Add';
    if ($resubscribe) {
      $action = 'Subscriber.AddAndResubscribe';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
        'Name' => $name,
      ),
    ));
  }

  /**
   * This encapsulates the check of whether this particular user unsubscribed once.
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   */
  function subscriberAddRedundant($email, $name, $list_id = null) {
    $added = $this
      ->subscriberAdd($email, $name, $list_id);
    if ($added && $added['Result']['Code'] == '204') {
      $subscribed = $this
        ->subscribersGetIsSubscribed($email, $list_id);

      // Must have unsubscribed, so resubscribe
      if ($subscribed['anyType'] == 'False') {

        // since we're internal, we'll just call the method with full parameters rather
        // than go through a secondary wrapper function.
        $added = $this
          ->subscriberAdd($email, $name, $list_id, true);
        return $added;
      }
    }
    return $added;
  }

  /**
   * @param string $email Email address.
   * @param string $name User's name.
   * @param mixed $fields Should be a $key => $value mapping. If there are more than one items for $key, let
   *        $value be a list of scalar values. Example: array( 'Interests' => array( 'xbox', 'wii' ) )
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscriber.AddWithCustomFields.aspx
   */
  function subscriberAddWithCustomFields($email, $name, $fields, $list_id = null, $resubscribe = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.AddWithCustomFields';
    if ($resubscribe) {
      $action = 'Subscriber.AddAndResubscribeWithCustomFields';
    }
    if (!is_array($fields)) {
      $fields = array();
    }
    $_fields = array(
      'SubscriberCustomField' => array(),
    );
    foreach ($fields as $k => $v) {
      if (is_array($v)) {
        foreach ($v as $nv) {
          $_fields['SubscriberCustomField'][] = array(
            'Key' => $k,
            'Value' => $nv,
          );
        }
      }
      else {
        $_fields['SubscriberCustomField'][] = array(
          'Key' => $k,
          'Value' => $v,
        );
      }
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
        'Name' => $name,
        'CustomFields' => $_fields,
      ),
    ));
  }

  /**
   * Same as subscriberAddRedundant() except with CustomFields.
   *
   * @param string $email Email address.
   * @param string $name User's name.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @return mixed A parsed response from the server, or null if something failed.
   */
  function subscriberAddWithCustomFieldsRedundant($email, $name, $fields, $list_id = null) {
    $added = $this
      ->subscriberAddWithCustomFields($email, $name, $fields, $list_id);
    if ($added && $added['Code'] == '0') {
      $subscribed = $this
        ->subscribersGetIsSubscribed($email);
      if ($subscribed == 'False') {
        $added = $this
          ->subscriberAddWithCustomFields($email, $name, $fields, $list_id, true);
        return $added;
      }
    }
    return $added;
  }

  /**
   * @param string $email Email address.
   * @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
   * @param boolean $check_subscribed If true, does the Subscribers.GetIsSubscribed API method instead.
   * @return mixed A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscriber.Unsubscribe.aspx
   */
  function subscriberUnsubscribe($email, $list_id = null, $check_subscribed = false) {
    if (!$list_id) {
      $list_id = $this->list_id;
    }
    $action = 'Subscriber.Unsubscribe';
    if ($check_subscribed) {
      $action = 'Subscribers.GetIsSubscribed';
    }
    return $this
      ->makeCall($action, array(
      'params' => array(
        'ListID' => $list_id,
        'Email' => $email,
      ),
    ));
  }

  /**
   * @return string A parsed response from the server, or null if something failed.
   * @see http://www.campaignmonitor.com/api/Subscribers.GetIsSubscribed.aspx
   */
  function subscribersGetIsSubscribed($email, $list_id = null) {
    return $this
      ->subscriberUnsubscribe($email, $list_id, true);
  }

  /**
   * Given an array of lists, indicate whether the $email is subscribed to each of those lists.
   *
   * @param string $email User's email
   * @param mixed $lists An associative array of lists to check against. Each key should be a List ID
   * @param boolean $no_assoc If true, only returns an array where each value indicates that the user is subscribed
   *        to that particular list. Otherwise, returns a fully associative array of $list_id => true | false.
   * @return mixed An array corresponding to $lists where true means the user is subscribed to that particular list.
   */
  function checkSubscriptions($email, $lists, $no_assoc = true) {
    $nlist = array();
    foreach ($lists as $lid => $misc) {
      $val = $this
        ->subscribersGetIsSubscribed($email, $lid);
      $val = $val != 'False';
      if ($no_assoc && $val) {
        $nlist[] = $lid;
      }
      elseif (!$no_assoc) {
        $nlist[$lid] = $val;
      }
    }
    return $nlist;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CMBase::$api property
CMBase::$debug_level property
CMBase::$method property
CMBase::checkSubscriptions function * Given an array of lists, indicate whether the $email is subscribed to each of those lists. * *
CMBase::CMBase function *
CMBase::makeCall function * The direct way to make an API call. This allows developers to include new API * methods that might not yet have a wrapper method as part of the package. * *
CMBase::subscriberAdd function * subscriberAdd() *
CMBase::subscriberAddRedundant function * This encapsulates the check of whether this particular user unsubscribed once. *
CMBase::subscriberAddWithCustomFields function *
CMBase::subscriberAddWithCustomFieldsRedundant function * Same as subscriberAddRedundant() except with CustomFields. * *
CMBase::subscribersGetActive function * Wrapper for Subscribers.GetActive. This method triples as Subscribers.GetUnsubscribed * and Subscribers.GetBounced when the very last parameter is overridden. * *
CMBase::subscribersGetBounced function *
CMBase::subscribersGetIsSubscribed function *
CMBase::subscribersGetUnsubscribed function *
CMBase::subscriberUnsubscribe function *