forena-8.x-1.x-dev/src/Context/DataContext.php

src/Context/DataContext.php
<?php
/**
 * @file
 * Implements \Drupal\forena\Context\DataContext
 */
namespace Drupal\forena\Context;
use Drupal\forena\AppService;
use SimpleXMLElement;
use DOMDocument;
use DOMXPath;

/**
 * The DataContext class holds all of the data contexts during the report
 * rendering process. The general idea is that during the report render, data
 * objects are pushed on the stack with the id's of the block or foreach objects
 * that invoke them.
 *
 * Rendering controls may then get current data contexts, and push data onto the
 * stack or pop data onto the stack. They may also use this method to get the
 * current data context from the stack.
 */
class DataContext {

  public $id;
  private $cur_context;  // The data of the xml;
  private $cur_context_xml; //
  private $data_sources = array();
  private $data_stack = array();
  private $id_stack = array();

  /**
   * Return the current data context
   *
   */
  public function currentContext() {
    return $this->cur_context;
  }

  /**
   * Return current context as key value pairs
   * Enter description here
   */
  public function currentContextArray() {
    $data = $this->cur_context;
    if (is_array($data)) {
      return $data;
    }

    if (is_object($data)) {
      // Get attributes
      $ret = get_object_vars($data);
      if (method_exists($data, 'attributes')) {
        foreach ($data->attributes() as $key => $value) {
          $ret[$key] = (string)$value;
        }
      }

    }
    else {
      $ret = (array)$data;
    }
    return $ret;

  }

  /**
   * Provides an api to the {=xpath} syntax that can be used
   * to evaluate expressions such as sum and count in a report.  We
   * need to use the DOM object here, because this method isn't exposed
   * with simplexml.
   *
   * @param \SimpleXMLElement $xml
   * @param string $path
   * @return \SimpleXMLElement
   */
  protected function simplexml_evaluate($xml, $path) {
    if (!method_exists($xml, 'xpath')) return '';
    $dom_node = dom_import_simplexml($xml);
    $dom_doc = new DOMDocument('');
    $dom_node = $dom_doc->importNode($dom_node, TRUE);
    $dom_doc->appendChild($dom_node);
    // Do we also need to call AppendChild?
    $xpath = new DOMXpath($dom_doc);
    $ret = $xpath->evaluate($path, $dom_node);
    return $ret;
  }


  static public function arrayToXml($a, &$xml=NULL) {
    if (!$xml) $xml = new SimpleXMLElement('<root/>');
    $tag = '';
    foreach($a as $k => $v) {
      if (preg_match('/^[0-9\-\.]/', $k)) {
        if (!$tag) $tag = "element";
      }
      else {
        $tag = $k;
      }
      if (is_array($v) || is_object($v)) {
        $node = $xml->addChild($tag, '');
        if ($tag != $k) {
          $node['key'] = $k;
        }
        $node['type'] = is_object($v) ? 'object' : 'array';
        DataContext::arrayToXml($v, $node);
      }
      else {
        $node = $xml->addChild($tag, htmlspecialchars($v));
        $node['key'] = $k;
      }

      $tag =  preg_replace( '/[^a-zA-Z0-9]/', '_', (string) $k);
    }
    return $xml;
  }

  /**
   * Get the value from the data.
   * This is used by token_replace method to extract the data based on the path provided.
   * @param $key
   *   The key/tken to search for.
   * @param string $context
   *   The id of the context to get the value from.
   * @return mixed
   *   Value or array returned for replacement
   */
  public function getValue($key, $context = '') {
    $retvar = '';
    // Default to theo current context
    $data = $this->currentContext();

    if ($context && $this->contextExists($context)) {

      $data = $this->getContext($context);

    }
    if (!preg_match('/[\=\@\[\/\]\(\)]/', $key)) {
      if (is_array($data)) {
        $retvar = @$data[$key];
      }
      elseif (is_object($data)) {
        $retvar = $data->$key;
      }
    }
    elseif (is_object($data) || is_array($data)) {
      if (is_array($data) ) {
        if ( !$this->cur_context_xml) $this->cur_context_xml = DataContext::arrayToXml($data);
        $data = $this->cur_context_xml;
      }
      elseif (!method_exists($data, 'xpath')) {
        if(method_exists($data, 'asXML')) {
          $xml = $data->asXML();
          if (!is_object($xml) && $xml) $xml = new SimpleXMLElement($xml);
          $this->cur_context_xml = $xml;
        }
      }

      if (strpos($key, '=')===0) {
        $retvar = $this->simplexml_evaluate($data, ltrim($key, '='));
      }
      else {
        $x ='';
        if (isset($data->$key)) {
          $x = $data->$key;
        }
        elseif (method_exists($data, 'xpath')) {
          $rows = @$data->xpath($key);
          if ($rows === FALSE) {
            drupal_set_message(t('Invalid field: "%s"', array('%s' => $key)), 'error', FALSE);
          }

          if ($rows) $x = $rows[0];
        }

        if ($x && is_object($x) && method_exists($x, 'asXML')) {
          $retvar = $x->asXML();
          // Check to see if there are child nodes
          // If so use asXML otherwise string cast.
          if ($retvar && strpos($retvar, '<')!==FALSE) {
            // Find the end of the first tag.
            $p = strpos($retvar, '>');
            $retvar = substr_replace($retvar, '', 0, $p+1);
            $p = strrpos($retvar, '<', -1);
            $retvar = substr_replace($retvar, '', $p, strlen($retvar) - $p);

          }
          else {
            $retvar = (string)$x;
          }
        }
        else {
          $retvar = &$x;
        }
      }
    }

    if (!is_array($retvar)) {
      if (is_object($retvar) && is_a($retvar, 'DOMNodeList')) {
        $retvar = $retvar->item(0);
        if ($retvar) ($retvar = trim($retvar->textContent));
      }
      else {
        $retvar = trim((string)$retvar);
      }
    }
    return $retvar;
  }

  /**
   * Allows override of a value for the current context.
   * @param string $key
   *   Poperty key to set.
   * @param string $value
   *   Property Value to set
   * @param string $context
   *   Value of context to set.
   */
  public function setValue($key, $value, $context='') {
    if (is_array($this->cur_context)) {
      $this->cur_context[$key] = $value;
      if ($this->cur_context_xml) $this->cur_context_xml->$key = $value;
    }
    elseif (is_object($this->cur_context)) {
      if (strpos($key, '@')===0) {
        $this->cur_context[trim($key, '@')] = $value;
      }
      else {
        $this->cur_context->$key = $value;
      }
    }
  }

  /**
   * Push a data context onto the data stacks
   * to make sure that we can address these using an
   * appropriate syntax.  I think we don't need data_stack
   * but i'm holding it there in case we develop a "relative" path syntax.
   * @param $data
   * @param $id
   */
  public function push($data, $id='') {
    $this->data_stack[] = $this->cur_context;
    $this->id_stack[] = $this->id;
    $this->id = $id;
    $this->cur_context = $data;
    $this->cur_context_xml = '';

    if ($id) {
      /*    if (@is_array($this->data_sources[$id]) && is_array($data)) {
       $data = array_merge($this->data_sources[$id], $data);
       }
       */
      $this->data_sources[$id] = $data;
    }
  }

  /**
   * @param $id
   *   Id of the data context to be set
   * @param $data
   *   Data of the data context to be set.
   */
  public function setContext($id, &$data) {
    if (is_object($data)) {
      $this->data_sources[$id] = $data;
    }
    else {
      $this->data_sources[$id] = &$data;
    }
  }

  /**
   * Remove data from the data stack.
   *
   * This will make data unavaiable when we leave the context of the current
   * nested reports.
   */
  public function pop() {
    $this->id = array_pop($this->id_stack);
    $this->cur_context = array_pop($this->data_stack);
    $this->cur_context_xml = '';
  }

  /**
   * Determines whether an array context exists for the specified id.
   * Returns true if the key exists othewise false
   * @param string $id
   * @return bool
   */
  public function contextExists($id) {

    $exists = FALSE;
    if (array_key_exists($id, $this->data_sources)) {
      $exists = TRUE;
    }
    else {
      // Check for module provided contexts;
      $contexts = AppService::instance()->getContextPlugins();
      if (isset($contexts[$id])) {
        $class = $contexts[$id];
        $object = new $class();
        $this->setContext($id, $object);
        $exists = TRUE;
      }
    }
    return $exists;
  }

  /**
   * Return a data context by id.
   *
   * @param string $id
   * @return \SimpleXMLElement | array
   *   Data Context
   */
  public function getContext($id) {
    return @$this->data_sources[$id];
  }

  public function dumpContext() {
    AppService::instance()->debug('cur_context ' . $this->id,
      '<br/>Stack<br/><pre>' . print_r($this->cur_context, 1) . '</pre>');
  }
  
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc