mustache_templates-8.x-1.0-beta4/src/Helpers/MustacheRenderTemplate.php

src/Helpers/MustacheRenderTemplate.php
<?php

namespace Drupal\mustache\Helpers;

use Drupal\Core\Url;
use Drupal\mustache\Exception\MustacheException;

/**
 * A helper class for building arrays to render Mustache templates.
 */
class MustacheRenderTemplate {

  /**
   * The render array representation.
   *
   * @var array
   */
  protected $render = ['#type' => 'mustache'];

  /**
   * MustacheRenderTemplate constructor.
   *
   * @param string $template
   *   The name of the Mustache template. It may be either defined
   *   by a hook implementation of hook_mustache_templates(), or
   *   is solely existent as a template file inside the theme.
   *   Example: The template name "foo_bar" is a template file being
   *   located inside the 'templates' theme directory as "foo-bar.mustache.tpl".
   *   Alternatively, use the second parameter $inline_content to specify the
   *   template directly with inline content instead of using template files.
   *   A template name must always be specified, regardless of being a
   *   a registered file or an inline template.
   * @param string $inline_content
   *   (Optional) Use this argument to specify the content of the template
   *   inline, instead of relying to a template file. This might be useful for
   *   quick template builds or dynamically generated template contents.
   */
  public function __construct($template, $inline_content = NULL) {
    $this->render['#template'] = $template;
    if (isset($inline_content)) {
      $this->render['#inline'] = $inline_content;
    }
  }

  /**
   * Creates a new MustacheRenderTemplate instance.
   *
   * @param string $template
   *   The name of the Mustache template. It may be either defined
   *   by a hook implementation of hook_mustache_templates(), or
   *   is solely existent as a template file inside the theme.
   *   Example: The template name "foo_bar" is a template file being
   *   located inside the 'templates' theme directory as "foo-bar.mustache.tpl".
   *   Alternatively, use the second parameter $inline_content to specify the
   *   template directly with inline content instead of using template files.
   *   A template name must always be specified, regardless of being a
   *   a registered file or an inline template.
   * @param string $inline_content
   *   (Optional) Use this argument to specify the content of the template
   *   inline, instead of relying to a template file. This might be useful for
   *   quick template builds or dynamically generated template contents.
   *
   * @return static
   */
  public static function build($template, $inline_content = NULL) {
    return new static($template, $inline_content);
  }

  /**
   * Set the data used for rendering.
   *
   * @param mixed $data
   *   An array or object which holds the data for PHP rendering.
   * @param array|null $select
   *   (Optional) The array holding the keys for nested data selection.
   *
   * @return $this
   *   The instance itself.
   */
  public function usingData($data, array $select = NULL) {
    $this->render['#data'] = $data;
    if (isset($select)) {
      $this->selectingSubsetFromData($select);
    }
    return $this;
  }

  /**
   * Define the url from where to receive Json-encoded data.
   *
   * @param \Drupal\Core\Url|string $url
   *   The url as object or string.
   * @param array|null $select
   *   (Optional) The array holding the keys for nested data selection.
   *
   * @return $this
   *   The instance itself.
   */
  public function usingDataFromUrl($url, array $select = NULL) {
    if (!($url instanceof Url) && !is_string($url)) {
      throw new MustacheException(t('The $url param must be a Url object or string.'));
    }
    if (is_string($url)) {
      try {
        $url = Url::fromUri($url);
      }
      catch (\InvalidArgumentException $e) {
        $url = Url::fromUserInput($url);
      }
    }
    $this->render['#data'] = $url;
    if (isset($select)) {
      $this->selectingSubsetFromData($select);
    }
    return $this;
  }

  /**
   * Specifies the subset to select out of the data.
   *
   * @param array $select
   *   The array holding the keys for nested data selection.
   *
   * @return $this
   *   The instance itself.
   */
  public function selectingSubsetFromData(array $select) {
    $this->render['#select'] = $select;
    return $this;
  }

  /**
   * Explicitly define partials that are being used by the scoped template.
   *
   * Explicitly defining partials before render execution, makes sure that
   * the corresponding templates are available at client-side runtime. For
   * server-side only rendering with Mustache.php, this step is not required,
   * because registered templates can be dynamically loaded at server-side
   * runtime. It can still be useful for server-side rendering when using inline
   * templates, that are not available as standalone template files.
   *
   * Please note, that partials can (and should) be explicitly defined via
   * hook_mustache_templates() within the 'default' values, if the scoped
   * template is defined by a module. That way partials will always be included
   * and cached, also for client-side rendering, and will not accidentally be
   * omitted when the scoped template is being used.
   *
   * @param array $partials
   *   An array keyed by partial / template name or a simple sequence of
   *   template names. Values can be either the (same) name of the template that
   *   is registered by a module or available in a theme, or a render array that
   *   is adding the template as inline Javascript. For the latter case, a
   *   render array of type 'mustache_template_js_inline' can be used. You can
   *   also set the second parameter $inline_only to TRUE and use an inline
   *   template content as simple string value.
   * @param bool $inline_only
   *   (Optional) Set to TRUE if you explicitly exclude registered templates,
   *   and use the given list of partials as inline templates only. Example:
   *   usingPartials(['my_inline_partial' => '{{foo}}'], TRUE)
   *
   * @return $this
   *   The instance itself.
   */
  public function usingPartials(array $partials, $inline_only = FALSE) {
    $this->render['#partials'] = [];
    $this->render['#inline_partials'] = [];
    foreach ($partials as $key => $value) {
      if ($inline_only || is_array($value)) {
        $this->render['#inline_partials'][$key] = $value;
      }
      else {
        $this->render['#partials'][$key] = $value;
      }
    }
    return $this;
  }

  /**
   * Define a render array to use as a placeholder.
   *
   * @param array $placeholder
   *   An arbitrary render array of the placeholder.
   *
   * @return $this
   *   The instance itself.
   */
  public function withPlaceholder(array $placeholder) {
    $this->render['#placeholder'] = $placeholder;
    return $this;
  }

  /**
   * Enable token processing.
   *
   * @param array $data
   *   (Optional) An array of Token data, like Token::replace() would expect.
   *   The processing takes care of global context data and entities being
   *   viewed, so there is no need to set them here.
   * @param array $options
   *   (Optional) An array of Token options, like Token::replace() would expect.
   *   If not set otherwise, empty tokens are being cleared by default.
   *
   * @return $this
   *   The instance itself.
   *
   * @see \Drupal\Core\Utility\Token
   */
  public function withTokens(array $data = [], array $options = []) {
    $this->render['#with_tokens'] = [
      'data' => $data,
      'options' => $options,
    ];
    return $this;
  }

  /**
   * Bind a form whose values should be included as data.
   *
   * The form values will be directly provided within the "form" key of the
   * data that is being passed to the template rendering. For example, if you
   * have an input field of name "search", then you can access that value in the
   * template with {{form.search}}. When a url is set for fetching Json-encoded
   * data (::usingDataFromUrl), then the form values will be added as query
   * string parameters. This enables your Json endpoint to return any requested
   * data for the given form input.
   *
   * It should be noted, when working with Drupal forms, mostly the form will
   * have a redirect after form submission. When a redirect happens, then the
   * form values are no longer available. The form redirect may be either
   * disabled, or the relevant arguments may be attached to the redirect url,
   * then extracted and (manually) passed to the data array.
   *
   * @param \Drupal\Core\Form\FormStateInterface|array $form_state_or_array
   *   The form state or an associative array with a 'values' key, which is an
   *   associative array of submitted form values. When using an array and
   *   client synchronization, optionally set a 'selector' key that will be used
   *   as the CSS selector to identify the current form element in the DOM, and
   *   to use whose current values during client synchronization. Example array:
   *   @code
   *   ['selector' => '#myformid', 'values' => ['myname' => 'myvalue']]
   *   // If you have client synchronization enabled, but no form is present
   *   // on that page, skip the 'selector' key:
   *   ['values' => ['myname' => 'myvalue']]
   *   @endcode
   *   If client synchronization is enabled and a selector is defined, the form
   *   will be automatically bound (when rendered) and the synchronization does
   *   require the form to be present in the DOM. If you do not have any
   *   server-side form state or values, and you solely want to use client
   *   synchronization, you can use SynchronizationOptions::usingFormValues()
   *   instead to set the CSS selector of the form element.
   *
   * @return $this
   *   The instance itself.
   */
  public function usingFormValues($form_state_or_array) {
    $this->render['#form'] = $form_state_or_array;
    return $this;
  }

  /**
   * Determine and configure client-side DOM content synchronization.
   *
   * @return \Drupal\mustache\Helpers\SynchronizationOptions
   *   The synchronization options to define.
   */
  public function withClientSynchronization() {
    return new SynchronizationOptions($this->render);
  }

  /**
   * Whether server-side data may be exposed into the browser client.
   *
   * This may be handy for example if you want to access or compare with
   * previously loaded data values.
   *
   * When this setting is enabled, the data that was retreived internally, or
   * fetched from an external HTTP endpoint, will be bypassed to the browser
   * client. That raw data would then be accessible and fully visible, thus
   * only use this option if you know what you are doing.
   *
   * @param bool $expose
   *   (Optional) Whether exposure shall be enabled (TRUE) or not (FALSE).
   *   By default, bypassing of server-side data is not enabled.
   *
   * @return $this
   *   The instance itself.
   */
  public function exposeData($expose = TRUE) {
    $this->render['#expose'] = $expose;
    return $this;
  }

  /**
   * Get the render array result.
   *
   * @return array
   *   The render array result.
   */
  public function &toRenderArray() {
    return $this->render;
  }

}

/**
 * Class SynchronizationOptions.
 *
 * @internal
 */
final class SynchronizationOptions {

  /**
   * The render array representation of the parent instance.
   *
   * @var array
   */
  private $render;

  /**
   * The synchronization item as array.
   *
   * @var array
   */
  private $item;

  /**
   * SynchronizationOptions constructor.
   *
   * @param array &$render_array
   *   The corresponding render array representation.
   */
  public function __construct(array &$render_array) {
    $this->render = &$render_array;
    if (!isset($render_array['#sync']['items'])) {
      $render_array['#sync']['items'] = [];
    }
    $i = count($render_array['#sync']['items']);

    $render_array['#sync']['items'][$i] = [
      'period' => 0,
      'delay' => 0,
    ];

    $this->item = &$render_array['#sync']['items'][$i];
  }

  /**
   * Define the url from where to receive Json-encoded data.
   *
   * @param \Drupal\Core\Url|string $url
   *   The url as object or string.
   * @param array|null $select
   *   (Optional) The array holding the keys for nested data selection.
   *
   * @return $this
   *   The instance itself.
   */
  public function usingDataFromUrl($url, array $select = NULL) {
    if (!($url instanceof Url) && !is_string($url)) {
      throw new MustacheException(t('The $url param must be a Url object or string.'));
    }
    if (is_string($url)) {
      try {
        $url = Url::fromUri($url);
      }
      catch (\InvalidArgumentException $e) {
        $url = Url::fromUserInput($url);
      }
    }
    $this->item['url'] = $url;
    if (isset($select)) {
      $this->selectingSubsetFromData($select);
    }
    return $this;
  }

  /**
   * Specifies the subset to select out of the data.
   *
   * @param array $select
   *   The array holding the keys for nested data selection.
   *
   * @return $this
   *   The instance itself.
   */
  public function selectingSubsetFromData(array $select) {
    $this->item['select'] = $select;
    return $this;
  }

  /**
   * Determine the interval in milliseconds to delay synchronization.
   *
   * @param int $milliseconds
   *   The interval in milliseconds to delay the synchronization start.
   *
   * @return $this
   *   The instance itself.
   */
  public function startsDelayed($milliseconds) {
    $this->item['delay'] = $milliseconds;
    return $this;
  }

  /**
   * Determine the interval in milliseconds to repeat synchronization.
   *
   * @param int $milliseconds
   *   The interval in milliseconds to repeat synchronization.
   *
   * @return $this
   *   The instance itself.
   */
  public function periodicallyRefreshesAt($milliseconds) {
    $this->item['period'] = $milliseconds;
    return $this;
  }

  /**
   * Determine the maximum number of synchronizations.
   *
   * @param int $n
   *   The maximum number of times to run the synchronization.
   *
   * @return $this
   *   The instance itself.
   */
  public function upToNTimes($n) {
    $this->item['limit'] = $n;
    return $this;
  }

  /**
   * Determine that the synchronization is being run only once.
   *
   * @return $this
   *   The instance itself.
   */
  public function once() {
    return $this->upToNTimes(1);
  }

  /**
   * Determine that the number of synchronizations is not limited.
   *
   * @return $this
   *   The instance itself.
   */
  public function unlimited() {
    return $this->upToNTimes(-1);
  }

  /**
   * Determine and configure a triggering element (optional).
   *
   * @param string $css_selector
   *   The CSS element selector.
   *
   * @return \Drupal\mustache\Helpers\TriggeringElementOptions
   *   The configurable options for the triggering element.
   */
  public function startsWhenElementWasTriggered($css_selector) {
    return new TriggeringElementOptions($css_selector, $this->render, $this->item);
  }

  /**
   * Determine the HTML tag to use (optional).
   *
   * By default, a div tag would wrap the rendered template.
   *
   * @param string $html_tag
   *   The HTML tag, e.g. span.
   *
   * @return $this
   *   The instance itself.
   */
  public function withWrapperTag($html_tag) {
    $this->render['#sync']['wrapper_tag'] = $html_tag;
    return $this;
  }

  /**
   * Determine to insert synchronized content into somewhere else.
   *
   * By default, synchronization happens by automatically wrapping the template
   * contents with a div or another tag type specified with ::withWrapperTag().
   * This option changes that behavior by inserting into another element.
   *
   * @param string $css_selector
   *   The CSS selector that identifies the insertion target. It should be made
   *   sure that this element actually exists in the DOM.
   *
   * @return $this
   *   The instance itself.
   */
  public function insertsInto($css_selector) {
    $this->render['#sync']['into'] = $css_selector;
    return $this;
  }

  /**
   * Set custom HTML attributes to the wrapper tag.
   *
   * @param array $attributes
   *   The HTML attributes to set.
   *
   * @return $this
   *   The instance itself.
   */
  public function withWrapperAttributes(array $attributes) {
    $this->render['#attributes'] = $attributes;
    return $this;
  }

  /**
   * Enables DOM tree transformation using morphdom.
   *
   * When using client side synchronization, by default the DOM content is being
   * completely replaced. While replacing an existing DOM tree with an entirely
   * new DOM tree is fast, all of the internal state associated with the
   * existing DOM nodes will be lost. Instead of replacing the existing DOM tree
   * with a new DOM tree you can use morphdom to to transform the existing DOM
   * tree to match the new DOM tree, minimizing the number of changes to the
   * existing DOM tree.
   *
   * Please note that you cannot use the "morph" setting together with the
   * "adjacent" setting (::insertsAt()). When "adjacent" is set, the "morph"
   * setting will be ignored.
   *
   * @param bool $morph
   *   Set to TRUE to enable morph DOM tree transformation.
   *
   * @return $this
   *   The instance itself.
   */
  public function morphing($morph = TRUE) {
    $this->item['morph'] = $morph;
    return $this;
  }

  /**
   * Set whether inner script content should be executed.
   *
   * Warning: Executing arbitrary scripts from untrusted sources
   * might lead your site to be vulnerable for XSS and other attacks.
   *
   * When enabled, Drupal attach and detach behaviors will
   * be executed too. In case you don't wish to include Drupal
   * behaviors, you need to explicitly disable them via
   * ::executesDrupalBehaviors(FALSE).
   *
   * @param bool $eval
   *   Set to TRUE to enable inner script execution.
   *   By default, inner script execution is not enabled.
   *
   * @return $this
   *   The instance itself.
   */
  public function executesInnerScripts($eval = TRUE) {
    $this->item['eval'] = $eval;
    return $this;
  }

  /**
   * Enable or disable Drupal's attach and detach behaviors.
   *
   * By default, Drupal behaviors are not enabled, unless
   * script execution is not enabled.
   *
   * @param bool $behaviors
   *   Set to TRUE to enable behaviors, or FALSE
   *   to explicitly disable them, e.g. when eval is TRUE.
   *
   * @return $this
   *   The instance itself.
   */
  public function executesDrupalBehaviors($behaviors = TRUE) {
    $this->item['behaviors'] = $behaviors;
    return $this;
  }

  /**
   * Determine whether and how the rendered content should be inserted.
   *
   * By default, the inner HTML of the wrapping element's would be
   * completely replaced by the synchronized content. When a position
   * is specified though, it will be inserted accordingly.
   *
   * @param string $position
   *   Can be either 'beforebegin', 'afterbegin', 'beforeend'
   *   or 'afterend'. Have a look at the Javascript function
   *   Element.insertAdjacentHTML for more information about
   *   inserting HTML with the position parameter.
   *
   * @return $this
   *   The instance itself.
   */
  public function insertsAt($position) {
    $this->item['adjacent'] = $position;
    return $this;
  }

  /**
   * Enable automatic incrementing.
   *
   * @return \Drupal\mustache\Helpers\IncrementOptions
   *   The increment options.
   */
  public function increments() {
    return new IncrementOptions($this->render, $this->item);
  }

  /**
   * Bind a form whose values should be included as data.
   *
   * The form values will be directly provided within the "form" key of the
   * data that is being passed to the template rendering. For example, if you
   * have an input field of name "search", then you can access that value in the
   * template with {{form.search}}. When a url is set for fetching Json-encoded
   * data (::usingDataFromUrl), then the form values will be added as query
   * string parameters. This enables your Json endpoint to return any requested
   * data for the given form input.
   *
   * @param string|array $selector_or_array
   *   The CSS selector that identifies the form element in the DOM. Only one
   *   form can be used, thus the first found form element that matches the
   *   given selector will be used. Alternatively you can specify an associative
   *   array whose keys are 'selector', 'el' and 'values' to prepopulate any
   *   known form values. Set the 'selector' key to NULL if there is no form
   *   present in the DOM. This might be useful when you have a page for
   *   submitted form values, but on that page the form itself is not present.
   *   Please note that the 'el' key must be present and set to NULL, as this
   *   key will be used for assigning the DOM element at client synchronization.
   *   Example array:
   *   @code
   *   ['selector' => NULL, 'el' => NULL, 'values' => ['myname' => 'myvalue']]
   *   @endcode
   *
   * @return $this
   *   The instance itself.
   */
  public function usingFormValues($selector_or_array = 'form') {
    $this->item['form'] = $selector_or_array;
    return $this;
  }

  /**
   * Bind values of an object that should be included as data.
   *
   * The values will be directly provided within the "object" key of the data
   * that is being passed to the template rendering. When a URL is set for
   * fetching Json-encoded data (::usingDataFromUrl), then the object values
   * will be added as query string parameters.
   *
   * The following precedence will be taken into account:
   * - If the data array contains data matching the given selection, that one
   *   will be used.
   * - Otherwise, if a global object (namely a property of window) exists, that
   *   one will be used.
   *
   * @param array|string $select
   *   Either a string with dot notation or a nested array that holds a sequence
   *   of keys for data selection. Examples:
   *   @code
   *   'drupalSettings.ajaxPageState.libraries'
   *   'drupalSettings.ajaxPageState.libraries+another.one'
   *   [['drupalSettings', 'ajaxPageState', 'libraries']]
   *   [['drupalSettings', 'ajaxPageState', 'libraries'], ['another', 'one']]
   *   @endcode
   *
   * @return $this
   *   The instance itself.
   */
  public function usingObjectValues($select) {
    $selects = is_array($select) ? $select : explode('+', $select);
    foreach ($selects as $i => $select) {
      if (is_string($select)) {
        $selects[$i] = explode('.', $select);
      }
    }
    if ($selects) {
      $this->item['object'] = $selects;
    }
    return $this;
  }

  /**
   * Define a maximum allowed age of fetched data.
   *
   * Define a value in milliseconds greater than 0, so that previously
   * fetched data can be reused instead of being fetched again from the Json
   * endpoint. The size of the data cache is very limited though, so that
   * redundant fetch calls might still occur when there are many variants of
   * urls to be used.
   *
   * @param int $max_age
   *   The maximum allowed age of data in milliseconds. The value must be
   *   unsigned, i.e. it must be equal to or greater than 0.
   *
   * @return $this
   *   The instance itself.
   */
  public function dataMayBeOfMaxAge($max_age) {
    $this->item['max_age'] = $max_age;
    return $this;
  }

  /**
   * Get the render array result.
   *
   * @return array
   *   The render array result.
   */
  public function &toRenderArray() {
    return $this->render;
  }

}

/**
 * Class IncrementOptions.
 *
 * @internal
 */
final class IncrementOptions {

  /**
   * The render array representation of the parent instance.
   *
   * @var array
   */
  protected $render;

  /**
   * The sync item as array representation.
   *
   * @var array
   */
  protected $syncItem;

  /**
   * The increment options.
   *
   * @var array
   */
  protected $increment;

  /**
   * IncrementOptions constructor.
   *
   * @param array &$render_array
   *   The corresponding render array representation.
   * @param array &$sync_item
   *   The sync item as array representation.
   */
  public function __construct(array &$render_array, array &$sync_item) {
    $this->render = &$render_array;
    $this->syncItem = &$sync_item;
    if (!isset($sync_item['increment'])) {
      $sync_item['increment'] = [];
    }
    $this->increment = &$sync_item['increment'];
  }

  /**
   * Define the url parameter to increment.
   *
   * @param string $key
   *   The url parameter. Default parameter would be 'page'.
   *
   * @return $this
   *   The instance itself.
   */
  public function atParamKey($key) {
    $this->increment['key'] = $key;
    return $this;
  }

  /**
   * Define the step size to increment.
   *
   * Example: When set to 5, the parameter value
   * is being incremented by +5.
   *
   * @param int $step
   *   The step size to set. Default step size is 1.
   *
   * @return $this
   *   The instance itself.
   */
  public function withStepSize($step) {
    $this->increment['step'] = $step;
    return $this;
  }

  /**
   * Define an offset from where to start incrementing.
   *
   * Example: When offset is set to 100 and step size is
   * set to 1, the first increment would be 101.
   *
   * @param int $offset
   *   The offset value to set. The default offset value is 0.
   *
   * @return $this
   *   The instance itself.
   */
  public function startingAt($offset) {
    $this->increment['offset'] = $offset;
    return $this;
  }

  /**
   * Define the maximum number of increments.
   *
   * @param int $n
   *   The limit of increments. Default is unlimited (-1).
   *
   * @return $this
   *   The instance itself.
   */
  public function upToNTimes($n) {
    $this->increment['max'] = $n;
    return $this;
  }

  /**
   * Define that the number of increments is unlimited.
   *
   * @return $this
   *   The instance itself.
   */
  public function unlimited() {
    return $this->upToNTimes(-1);
  }

  /**
   * Define that the increment is a loop.
   *
   * When enabled, the increment starts back at the offset, once
   * it has reached its increment limit. It would also restart
   * increments once it has received an empty or "not found" result
   * from the specified url endpoint.
   *
   * @param bool $loops
   *   Set to TRUE to enable, or FALSE to turn off looping.
   *   Default is enabled.
   *
   * @return $this
   *   The instance itself.
   */
  public function loops($loops = TRUE) {
    $this->increment['loop'] = $loops;
    return $this;
  }

}

/**
 * Class TriggeringElementOptions.
 *
 * @internal
 */
final class TriggeringElementOptions {

  /**
   * The CSS element selector.
   *
   * @var string
   */
  protected $selector;

  /**
   * The render array representation of the parent instance.
   *
   * @var array
   */
  protected $render;

  /**
   * The sync item as array representation.
   *
   * @var array
   */
  protected $syncItem;

  /**
   * The part of the render array which holds the trigger options.
   *
   * @var array
   */
  protected $trigger;

  /**
   * TriggeringElementOptions constructor.
   *
   * @param string $css_selector
   *   The CSS element selector.
   * @param array &$render_array
   *   The corresponding render array representation.
   * @param array &$sync_item
   *   The sync item as array representation.
   */
  public function __construct($css_selector, array &$render_array, array &$sync_item) {
    $this->render = &$render_array;
    $this->syncItem = &$sync_item;
    $this->selector = $css_selector;
    if (!isset($sync_item['trigger'])) {
      $sync_item['trigger'] = [];
    }
    $i = count($sync_item['trigger']);
    $sync_item['trigger'][$i] = [$css_selector, 'load', 1];
    $this->trigger = &$sync_item['trigger'][$i];
  }

  /**
   * Determine the Javascript event which would trigger at the element.
   *
   * @param string $event
   *   The Javascript triggering event, e.g. 'click'.
   *
   * @return $this
   *   The instance itself.
   */
  public function atEvent($event) {
    $this->trigger[1] = $event;
    return $this;
  }

  /**
   * Determine how many times the synchronization is being triggered.
   *
   * @param int $n
   *   The maximum number of times to trigger the synchronization.
   *
   * @return $this
   *   The instance itself.
   */
  public function upToNTimes($n) {
    $this->trigger[2] = $n;
    return $this;
  }

  /**
   * Determine that the synchronization is being triggered only once.
   *
   * @return $this
   *   The instance itself.
   */
  public function once() {
    return $this->upToNTimes(1);
  }

  /**
   * Determine that the synchronization is always being triggered.
   *
   * @return $this
   *   The instance itself.
   */
  public function always() {
    return $this->upToNTimes(-1);
  }

  /**
   * Get the render array result.
   *
   * @return array
   *   The render array result.
   */
  public function &toRenderArray() {
    return $this->render;
  }

}

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

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