You are here

class DataContext in Forena Reports 8

Same name and namespace in other branches
  1. 7.5 src/Context/DataContext.php \Drupal\forena\Context\DataContext

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.

Hierarchy

Expanded class hierarchy of DataContext

7 files declare their use of DataContext
ContextBase.php in src/FrxPlugin/Context/ContextBase.php
DataManager.php in src/DataManager.php
DataManager.inc Enter description here ... @author davidmetzler
FrxAPI.php in src/FrxAPI.php
FrxAPI.incL General Forena Reporting Class
FrxXML.php in src/FrxPlugin/Renderer/FrxXML.php
FrxXML Just render the XML source data. Look at the FrxRender class to see a full list of properties that can be used here.
RendererBase.php in src/FrxPlugin/Renderer/RendererBase.php
FrxRenderer.php Base class for FrxAPI custom Renderer @author davidmetzler

... See full list

File

src/Context/DataContext.php, line 22
Implements \Drupal\forena\Context\DataContext

Namespace

Drupal\forena\Context
View source
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;
  }
  public static 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>');
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DataContext::$cur_context private property
DataContext::$cur_context_xml private property
DataContext::$data_sources private property
DataContext::$data_stack private property
DataContext::$id public property
DataContext::$id_stack private property
DataContext::arrayToXml public static function
DataContext::contextExists public function Determines whether an array context exists for the specified id. Returns true if the key exists othewise false
DataContext::currentContext public function Return the current data context
DataContext::currentContextArray public function Return current context as key value pairs Enter description here
DataContext::dumpContext public function
DataContext::getContext public function Return a data context by id.
DataContext::getValue public function Get the value from the data. This is used by token_replace method to extract the data based on the path provided.
DataContext::pop public function Remove data from the data stack.
DataContext::push public function 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.
DataContext::setContext public function
DataContext::setValue public function Allows override of a value for the current context.
DataContext::simplexml_evaluate protected function 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.