diff --git a/src/View/SSViewer.php b/src/View/SSViewer.php
index 778916193..c69953243 100644
--- a/src/View/SSViewer.php
+++ b/src/View/SSViewer.php
@@ -48,42 +48,6 @@ class SSViewer implements Flushable
*/
const DEFAULT_THEME = '$default';
- /**
- * @config
- * @var boolean $source_file_comments
- */
- private static $source_file_comments = false;
-
- /**
- * @ignore
- */
- private static $template_cache_flushed = false;
-
- /**
- * @ignore
- */
- private static $cacheblock_cache_flushed = false;
-
- /**
- * @var array $templates List of templates to select from
- */
- protected $templates = null;
-
- /**
- * @var string $chosen Absolute path to chosen template file
- */
- protected $chosen = null;
-
- /**
- * @var array Templates to use when looking up 'Layout' or 'Content'
- */
- protected $subTemplates = null;
-
- /**
- * @var boolean
- */
- protected $rewriteHashlinks = true;
-
/**
* @config
* @var string A list (highest priority first) of themes to use
@@ -109,6 +73,61 @@ class SSViewer implements Flushable
*/
private static $theme_enabled = true;
+ /**
+ * Default prepended cache key for partial caching
+ *
+ * @config
+ * @var string
+ */
+ private static $global_key = '$CurrentReadingMode, $CurrentUser.ID';
+
+ /**
+ * @config
+ * @var boolean $source_file_comments
+ */
+ private static $source_file_comments = false;
+
+ /**
+ * @config
+ * @var boolean
+ */
+ private static $rewrite_hash_links = true;
+
+ /**
+ * @ignore
+ */
+ private static $template_cache_flushed = false;
+
+ /**
+ * @ignore
+ */
+ private static $cacheblock_cache_flushed = false;
+
+ /**
+ * @var array $topLevel List of items being processed
+ */
+ protected static $topLevel = [];
+
+ /**
+ * @var array $templates List of templates to select from
+ */
+ protected $templates = null;
+
+ /**
+ * @var string $chosen Absolute path to chosen template file
+ */
+ protected $chosen = null;
+
+ /**
+ * @var array Templates to use when looking up 'Layout' or 'Content'
+ */
+ protected $subTemplates = null;
+
+ /**
+ * @var boolean
+ */
+ protected $rewriteHashlinks = true;
+
/**
* @var boolean
*/
@@ -119,13 +138,42 @@ class SSViewer implements Flushable
*/
protected $parser;
- /*
- * Default prepended cache key for partial caching
- *
- * @var string
- * @config
- */
- private static $global_key = '$CurrentReadingMode, $CurrentUser.ID';
+ /**
+ * @var CacheInterface
+ */
+ protected $partialCacheStore = null;
+
+ /**
+ * @param string|array $templates If passed as a string with .ss extension, used as the "main" template.
+ * If passed as an array, it can be used for template inheritance (first found template "wins").
+ * Usually the array values are PHP class names, which directly correlate to template names.
+ *
+ * array('MySpecificPage', 'MyPage', 'Page')
+ *
+ * @param TemplateParser $parser
+ */
+ public function __construct($templates, TemplateParser $parser = null)
+ {
+ if ($parser) {
+ $this->setParser($parser);
+ }
+
+ $this->setTemplate($templates);
+
+ if (!$this->chosen) {
+ $message = 'None of the following templates could be found: ';
+ $message .= print_r($templates, true);
+
+ $themes = self::get_themes();
+ if (!$themes) {
+ $message .= ' (no theme in use)';
+ } else {
+ $message .= ' in themes "' . print_r($themes, true) . '"';
+ }
+
+ user_error($message, E_USER_WARNING);
+ }
+ }
/**
* Triggered early in the request when someone requests a flush.
@@ -163,6 +211,11 @@ class SSViewer implements Flushable
SSViewer::config()->set('themes', $themes);
}
+ /**
+ * Add to the list of active themes to apply
+ *
+ * @param array $themes
+ */
public static function add_themes($themes = [])
{
$currentThemes = SSViewer::get_themes();
@@ -171,6 +224,11 @@ class SSViewer implements Flushable
SSViewer::set_themes(array_values(array_unique($finalThemes)));
}
+ /**
+ * Get the list of active themes
+ *
+ * @return array
+ */
public static function get_themes()
{
$default = [self::DEFAULT_THEME];
@@ -223,7 +281,8 @@ class SSViewer implements Flushable
'SSViewer::get_templates_by_class() expects a valid class name as its first parameter.'
);
}
- $templates = array();
+
+ $templates = [];
$classes = array_reverse(ClassInfo::ancestry($classOrObject));
foreach ($classes as $class) {
$template = $class . $suffix;
@@ -239,41 +298,26 @@ class SSViewer implements Flushable
break;
}
}
+
return $templates;
}
/**
- * @param string|array $templates If passed as a string with .ss extension, used as the "main" template.
- * If passed as an array, it can be used for template inheritance (first found template "wins").
- * Usually the array values are PHP class names, which directly correlate to template names.
- *
- * array('MySpecificPage', 'MyPage', 'Page')
- *
- * @param TemplateParser $parser
+ * Get the current item being processed
+ *
+ * @return ViewableData
*/
- public function __construct($templates, TemplateParser $parser = null)
+ public static function topLevel()
{
- if ($parser) {
- $this->setParser($parser);
- }
-
- $this->setTemplate($templates);
-
- if (!$this->chosen) {
- $message = 'None of the following templates could be found: ';
- $message .= print_r($templates, true);
-
- $themes = self::get_themes();
- if (!$themes) {
- $message .= ' (no theme in use)';
- } else {
- $message .= ' in themes "' . print_r($themes, true) . '"';
- }
-
- user_error($message, E_USER_WARNING);
+ if (SSViewer::$topLevel) {
+ return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1];
}
+ return null;
}
+ /**
+ * @param string|array $templates
+ */
public function setTemplate($templates)
{
$this->templates = $templates;
@@ -327,22 +371,6 @@ class SSViewer implements Flushable
return (bool)ThemeResourceLoader::inst()->findTemplate($templates, self::get_themes());
}
- /**
- * @config
- * @var boolean
- */
- private static $rewrite_hash_links = true;
-
- protected static $topLevel = array();
-
- public static function topLevel()
- {
- if (SSViewer::$topLevel) {
- return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1];
- }
- return null;
- }
-
/**
* Call this to disable rewriting of links. This is useful in Ajax applications.
* It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process();
@@ -354,6 +382,9 @@ class SSViewer implements Flushable
return $this;
}
+ /**
+ * @return string
+ */
public function exists()
{
return $this->chosen;
@@ -362,7 +393,6 @@ class SSViewer implements Flushable
/**
* @param string $identifier A template name without '.ss' extension or path
* @param string $type The template type, either "main", "Includes" or "Layout"
- *
* @return string Full system path to a template file
*/
public static function getTemplateFileByType($identifier, $type = null)
@@ -410,11 +440,6 @@ class SSViewer implements Flushable
}
}
- /**
- * @var CacheInterface
- */
- protected $partialCacheStore = null;
-
/**
* Set the cache object to use when storing / retrieving partial cache blocks.
*
@@ -432,7 +457,11 @@ class SSViewer implements Flushable
*/
public function getPartialCacheStore()
{
- return $this->partialCacheStore ? $this->partialCacheStore : Injector::inst()->get(CacheInterface::class . '.cacheblock');
+ if ($this->partialCacheStore) {
+ return $this->partialCacheStore;
+ }
+
+ return Injector::inst()->get(CacheInterface::class . '.cacheblock');
}
/**
@@ -504,7 +533,7 @@ class SSViewer implements Flushable
$template = $this->chosen;
$cacheFile = TEMP_FOLDER . "/.cache"
- . str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
+ . str_replace(['\\','/',':'], '.', Director::makeRelative(realpath($template)));
$lastEdited = filemtime($template);
if (!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
@@ -516,11 +545,11 @@ class SSViewer implements Flushable
fclose($fh);
}
- $underlay = array('I18NNamespace' => basename($template));
+ $underlay = ['I18NNamespace' => basename($template)];
// Makes the rendered sub-templates available on the parent item,
// through $Content and $Layout placeholders.
- foreach (array('Content', 'Layout') as $subtemplate) {
+ foreach (['Content', 'Layout'] as $subtemplate) {
// Detect sub-template to use
$sub = $this->getSubtemplateFor($subtemplate);
if (!$sub) {
@@ -557,7 +586,9 @@ class SSViewer implements Flushable
if ($this->rewriteHashlinks && $rewrite) {
if (strpos($output, '";
+ $thisURLRelativeToBase = <<
+PHP;
} else {
$thisURLRelativeToBase = Convert::raw2att(preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI']));
}
@@ -642,6 +673,12 @@ class SSViewer implements Flushable
return $v->process($data, $arguments);
}
+ /**
+ * Parse given template contents
+ *
+ * @param string $content The template contents
+ * @param string $template The template file name
+ */
public function parseTemplateContent($content, $template = "")
{
return $this->getParser()->compileString(
@@ -654,6 +691,8 @@ class SSViewer implements Flushable
/**
* Returns the filenames of the template that will be rendered. It is a map that may contain
* 'Content' & 'Layout', and will have to contain 'main'
+ *
+ * @return array
*/
public function templates()
{
diff --git a/src/View/SSViewer_BasicIteratorSupport.php b/src/View/SSViewer_BasicIteratorSupport.php
index fabfc91d2..a8762fe12 100644
--- a/src/View/SSViewer_BasicIteratorSupport.php
+++ b/src/View/SSViewer_BasicIteratorSupport.php
@@ -8,7 +8,6 @@ namespace SilverStripe\View;
*/
class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider
{
-
/**
* @var int
*/
@@ -19,9 +18,12 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider
*/
protected $iteratorTotalItems;
+ /**
+ * @return array
+ */
public static function get_template_iterator_variables()
{
- return array(
+ return [
'First',
'Last',
'FirstLast',
@@ -35,7 +37,7 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider
'TotalItems',
'Modulus',
'MultipleOf',
- );
+ ];
}
/**
@@ -183,6 +185,7 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider
/**
* Returns the modulus of the numerical position of the item in the data set.
* The count starts from $startIndex, which defaults to 1.
+ *
* @param int $mod The number to perform Mod operation to.
* @param int $startIndex Number to start count from.
* @return int
@@ -196,6 +199,7 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider
* Returns true or false depending on if the pos of the iterator is a multiple of a specific number.
* So, <% if MultipleOf(3) %> would return true on indexes: 3,6,9,12,15, etc.
* The count starts from $offset, which defaults to 1.
+ *
* @param int $factor The multiple of which to return
* @param int $offset Number to start count from.
* @return bool
diff --git a/src/View/SSViewer_DataPresenter.php b/src/View/SSViewer_DataPresenter.php
index f80772b41..cf609d117 100644
--- a/src/View/SSViewer_DataPresenter.php
+++ b/src/View/SSViewer_DataPresenter.php
@@ -40,95 +40,126 @@ class SSViewer_DataPresenter extends SSViewer_Scope
/**
* Underlay variables. Concede precedence to overlay variables or anything from the current scope
- * @var array|null
+ *
+ * @var array
*/
protected $underlay;
- public function __construct($item, $overlay = null, $underlay = null, $inheritedScope = null)
- {
+ /**
+ * @var object $item
+ * @var array $overlay
+ * @var array $underlay
+ * @var SSViewer_Scope $inheritedScope
+ */
+ public function __construct(
+ $item,
+ array $overlay = null,
+ array $underlay = null,
+ SSViewer_Scope $inheritedScope = null
+ ) {
parent::__construct($item, $inheritedScope);
- // Build up global property providers array only once per request
- if (self::$globalProperties === null) {
- self::$globalProperties = array();
- // Get all the exposed variables from all classes that implement the TemplateGlobalProvider interface
- $this->createCallableArray(
- self::$globalProperties,
- TemplateGlobalProvider::class,
- "get_template_global_variables"
- );
- }
+ $this->overlay = $overlay ?: [];
+ $this->underlay = $underlay ?: [];
- // Build up iterator property providers array only once per request
- if (self::$iteratorProperties === null) {
- self::$iteratorProperties = array();
- // Get all the exposed variables from all classes that implement the TemplateIteratorProvider interface
- // //call non-statically
- $this->createCallableArray(
- self::$iteratorProperties,
- TemplateIteratorProvider::class,
- "get_template_iterator_variables",
- true
- );
- }
-
- $this->overlay = $overlay ? $overlay : array();
- $this->underlay = $underlay ? $underlay : array();
+ $this->cacheGlobalProperties();
+ $this->cacheIteratorProperties();
}
- protected function createCallableArray(&$extraArray, $interfaceToQuery, $variableMethod, $createObject = false)
+ /**
+ * Build cache of global properties
+ */
+ protected function cacheGlobalProperties()
{
- $implementers = ClassInfo::implementorsOf($interfaceToQuery);
- if ($implementers) {
- foreach ($implementers as $implementer) {
+ if (self::$globalProperties !== null) {
+ return;
+ }
+
+ self::$globalProperties = $this->getPropertiesFromProvider(
+ TemplateGlobalProvider::class,
+ 'get_template_global_variables'
+ );
+ }
+
+ /**
+ * Build cache of global iterator properties
+ */
+ protected function cacheIteratorProperties()
+ {
+ if (self::$iteratorProperties !== null) {
+ return;
+ }
+
+ self::$iteratorProperties = $this->getPropertiesFromProvider(
+ TemplateIteratorProvider::class,
+ 'get_template_iterator_variables',
+ true // Call non-statically
+ );
+ }
+
+ /**
+ * @var string $interfaceToQuery
+ * @var string $variableMethod
+ * @var boolean $createObject
+ * @return array
+ */
+ protected function getPropertiesFromProvider($interfaceToQuery, $variableMethod, $createObject = false)
+ {
+ $methods = [];
+
+ $implementors = ClassInfo::implementorsOf($interfaceToQuery);
+ if ($implementors) {
+ foreach ($implementors as $implementor) {
// Create a new instance of the object for method calls
if ($createObject) {
- $implementer = new $implementer();
+ $implementor = new $implementor();
+ $exposedVariables = $implementor->$variableMethod();
+ } else {
+ $exposedVariables = $implementor::$variableMethod();
}
- // Get the exposed variables
- $exposedVariables = call_user_func(array($implementer, $variableMethod));
-
foreach ($exposedVariables as $varName => $details) {
if (!is_array($details)) {
- $details = array(
+ $details = [
'method' => $details,
'casting' => ViewableData::config()->uninherited('default_cast')
- );
+ ];
}
- // If just a value (and not a key => value pair), use it for both key and value
+ // If just a value (and not a key => value pair), use method name for both key and value
if (is_numeric($varName)) {
$varName = $details['method'];
}
// Add in a reference to the implementing class (might be a string class name or an instance)
- $details['implementer'] = $implementer;
+ $details['implementor'] = $implementor;
// And a callable array
if (isset($details['method'])) {
- $details['callable'] = array($implementer, $details['method']);
+ $details['callable'] = [$implementor, $details['method']];
}
// Save with both uppercase & lowercase first letter, so either works
$lcFirst = strtolower($varName[0]) . substr($varName, 1);
- $extraArray[$lcFirst] = $details;
- $extraArray[ucfirst($varName)] = $details;
+ $result[$lcFirst] = $details;
+ $result[ucfirst($varName)] = $details;
}
}
}
+
+ return $result;
}
/**
- * Get the injected value
+ * Look up injected value - it may be part of an "overlay" (arguments passed to <% include %>),
+ * set on the current item, part of an "underlay" ($Layout or $Content), or an iterator/global property
*
* @param string $property Name of property
* @param array $params
* @param bool $cast If true, an object is always returned even if not an object.
- * @return array Result array with the keys 'value' for raw value, or 'obj' if contained in an object
- * @throws InvalidArgumentException
+ * @return array|null
*/
- public function getInjectedValue($property, $params, $cast = true)
+ public function getInjectedValue($property, array $params, $cast = true)
{
// Get source for this value
$source = $this->getValueSource($property);
@@ -139,7 +170,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope
// Look up the value - either from a callable, or from a directly provided value
$res = [];
if (isset($source['callable'])) {
- $res['value'] = call_user_func_array($source['callable'], $params);
+ $res['value'] = $source['callable'](...$params);
} elseif (isset($source['value'])) {
$res['value'] = $source['value'];
} else {
@@ -152,6 +183,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope
if ($cast) {
$res['obj'] = $this->castValue($res['value'], $source);
}
+
return $res;
}
@@ -238,6 +270,9 @@ class SSViewer_DataPresenter extends SSViewer_Scope
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
public function getObj($name, $arguments = [], $cache = false, $cacheName = null)
{
$result = $this->getInjectedValue($name, (array)$arguments);
@@ -247,34 +282,31 @@ class SSViewer_DataPresenter extends SSViewer_Scope
return parent::getObj($name, $arguments, $cache, $cacheName);
}
+ /**
+ * {@inheritdoc}
+ */
public function __call($name, $arguments)
{
- //extract the method name and parameters
- $property = $arguments[0]; //the name of the public function being called
+ // Extract the method name and parameters
+ $property = $arguments[0]; // The name of the public function being called
- //the public function parameters in an array
- if (isset($arguments[1]) && $arguments[1] != null) {
- $params = $arguments[1];
- } else {
- $params = array();
- }
+ // The public function parameters in an array
+ $params = (isset($arguments[1])) ? (array)$arguments[1] : [];
$val = $this->getInjectedValue($property, $params);
if ($val) {
$obj = $val['obj'];
if ($name === 'hasValue') {
- $res = $obj instanceof ViewableData
- ? $obj->exists()
- : (bool)$obj;
+ $result = ($obj instanceof ViewableData) ? $obj->exists() : (bool)$obj;
} else {
- // XML_val
- $res = $obj->forTemplate();
+ $result = $obj->forTemplate(); // XML_val
}
+
$this->resetLocalScope();
- return $res;
- } else {
- return parent::__call($name, $arguments);
+ return $result;
}
+
+ return parent::__call($name, $arguments);
}
/**
@@ -336,18 +368,18 @@ class SSViewer_DataPresenter extends SSViewer_Scope
// Then for iterator-specific overrides
if (array_key_exists($property, self::$iteratorProperties)) {
$source = self::$iteratorProperties[$property];
- /** @var TemplateIteratorProvider $implementer */
- $implementer = $source['implementer'];
+ /** @var TemplateIteratorProvider $implementor */
+ $implementor = $source['implementor'];
if ($this->itemIterator) {
// Set the current iterator position and total (the object instance is the first item in
// the callable array)
- $implementer->iteratorProperties(
+ $implementor->iteratorProperties(
$this->itemIterator->key(),
$this->itemIteratorTotal
);
} else {
// If we don't actually have an iterator at the moment, act like a list of length 1
- $implementer->iteratorProperties(0, 1);
+ $implementor->iteratorProperties(0, 1);
}
return $source;
}
diff --git a/src/View/SSViewer_FromString.php b/src/View/SSViewer_FromString.php
index 653947cbf..93fb57b86 100644
--- a/src/View/SSViewer_FromString.php
+++ b/src/View/SSViewer_FromString.php
@@ -9,9 +9,9 @@ use SilverStripe\Core\Config\Config;
*/
class SSViewer_FromString extends SSViewer
{
-
/**
* The global template caching behaviour if no instance override is specified
+ *
* @config
* @var bool
*/
@@ -19,6 +19,7 @@ class SSViewer_FromString extends SSViewer
/**
* The template to use
+ *
* @var string
*/
protected $content;
@@ -30,6 +31,10 @@ class SSViewer_FromString extends SSViewer
*/
protected $cacheTemplate;
+ /**
+ * @param string $content
+ * @param TemplateParser $parser
+ */
public function __construct($content, TemplateParser $parser = null)
{
if ($parser) {
@@ -39,6 +44,9 @@ class SSViewer_FromString extends SSViewer
$this->content = $content;
}
+ /**
+ * {@inheritdoc}
+ */
public function process($item, $arguments = null, $scope = null)
{
$hash = sha1($this->content);
diff --git a/src/View/SSViewer_Scope.php b/src/View/SSViewer_Scope.php
index 45e9353b1..fdbce4c73 100644
--- a/src/View/SSViewer_Scope.php
+++ b/src/View/SSViewer_Scope.php
@@ -26,7 +26,6 @@ use Iterator;
*/
class SSViewer_Scope
{
-
const ITEM = 0;
const ITEM_ITERATOR = 1;
const ITEM_ITERATOR_TOTAL = 2;
@@ -35,12 +34,18 @@ class SSViewer_Scope
const CURRENT_INDEX = 5;
const ITEM_OVERLAY = 6;
- // The stack of previous "global" items
- // An indexed array of item, item iterator, item iterator total, pop index, up index, current index & parent overlay
- private $itemStack = array();
+ /**
+ * The stack of previous items ("scopes") - an indexed array of: item, item iterator, item iterator total,
+ * pop index, up index, current index & parent overlay
+ *
+ * @var array
+ */
+ private $itemStack = [];
/**
* The current "global" item (the one any lookup starts from)
+ *
+ * @var object
*/
protected $item;
@@ -51,44 +56,88 @@ class SSViewer_Scope
*/
protected $itemIterator;
- //Total number of items in the iterator
+ /**
+ * Total number of items in the iterator
+ *
+ * @var int
+ */
protected $itemIteratorTotal;
- // A pointer into the item stack for which item should be scope on the next pop call
+ /**
+ * A pointer into the item stack for the item that will become the active scope on the next pop call
+ *
+ * @var int
+ */
private $popIndex;
- // A pointer into the item stack for which item is "up" from this one
- private $upIndex = null;
+ /**
+ * A pointer into the item stack for which item is "up" from this one
+ *
+ * @var int
+ */
+ private $upIndex;
- // A pointer into the item stack for which item is this one (or null if not in stack yet)
- private $currentIndex = null;
+ /**
+ * A pointer into the item stack for which the active item (or null if not in stack yet)
+ *
+ * @var int
+ */
+ private $currentIndex;
- private $localIndex;
+ /**
+ * A store of copies of the main item stack, so it's preserved during a lookup from local scope
+ * (which may push/pop items to/from the main item stack)
+ *
+ * @var array
+ */
+ private $localStack = [];
- public function __construct($item, $inheritedScope = null)
+ /**
+ * The index of the current item in the main item stack, so we know where to restore the scope
+ * stored in $localStack.
+ *
+ * @var int
+ */
+ private $localIndex = 0;
+
+ /**
+ * @var object $item
+ * @var SSViewer_Scope $inheritedScope
+ */
+ public function __construct($item, SSViewer_Scope $inheritedScope = null)
{
$this->item = $item;
- $this->localIndex = 0;
- $this->localStack = array();
- if ($inheritedScope instanceof SSViewer_Scope) {
- $this->itemIterator = $inheritedScope->itemIterator;
- $this->itemIteratorTotal = $inheritedScope->itemIteratorTotal;
- $this->itemStack[] = array($this->item, $this->itemIterator, $this->itemIteratorTotal, null, null, 0);
- } else {
- $this->itemStack[] = array($this->item, null, 0, null, null, 0);
- }
+
+ $this->itemIterator = ($inheritedScope) ? $inheritedScope->itemIterator : null;
+ $this->itemIteratorTotal = ($inheritedScope) ? $inheritedScope->itemIteratorTotal : 0;
+ $this->itemStack[] = [$this->item, $this->itemIterator, $this->itemIteratorTotal, null, null, 0];
}
+ /**
+ * Returns the current "active" item
+ *
+ * @return object
+ */
public function getItem()
{
return $this->itemIterator ? $this->itemIterator->current() : $this->item;
}
- /** Called at the start of every lookup chain by SSTemplateParser to indicate a new lookup from local scope */
+ /**
+ * Called at the start of every lookup chain by SSTemplateParser to indicate a new lookup from local scope
+ *
+ * @return self
+ */
public function locally()
{
- list($this->item, $this->itemIterator, $this->itemIteratorTotal, $this->popIndex, $this->upIndex,
- $this->currentIndex) = $this->itemStack[$this->localIndex];
+ list(
+ $this->item,
+ $this->itemIterator,
+ $this->itemIteratorTotal,
+ $this->popIndex,
+ $this->upIndex,
+ $this->currentIndex
+ ) = $this->itemStack[$this->localIndex];
// Remember any un-completed (resetLocalScope hasn't been called) lookup chain. Even if there isn't an
// un-completed chain we need to store an empty item, as resetLocalScope doesn't know the difference later
@@ -97,16 +146,33 @@ class SSViewer_Scope
return $this;
}
+ /**
+ * Reset the local scope - restores saved state to the "global" item stack. Typically called after
+ * a lookup chain has been completed
+ */
public function resetLocalScope()
{
+ // Restore previous un-completed lookup chain if set
$previousLocalState = $this->localStack ? array_pop($this->localStack) : null;
-
array_splice($this->itemStack, $this->localIndex + 1, count($this->itemStack), $previousLocalState);
- list($this->item, $this->itemIterator, $this->itemIteratorTotal, $this->popIndex, $this->upIndex,
- $this->currentIndex) = end($this->itemStack);
+ list(
+ $this->item,
+ $this->itemIterator,
+ $this->itemIteratorTotal,
+ $this->popIndex,
+ $this->upIndex,
+ $this->currentIndex
+ ) = end($this->itemStack);
}
+ /**
+ * @param string $name
+ * @param array $arguments
+ * @param bool $cache
+ * @param string $cacheName
+ * @return mixed
+ */
public function getObj($name, $arguments = [], $cache = false, $cacheName = null)
{
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
@@ -128,15 +194,25 @@ class SSViewer_Scope
user_error('Up called when we\'re already at the top of the scope', E_USER_ERROR);
}
- list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
- $this->currentIndex) = $this->itemStack[$this->upIndex];
+ list(
+ $this->item,
+ $this->itemIterator,
+ $this->itemIteratorTotal,
+ /* dud */,
+ $this->upIndex,
+ $this->currentIndex
+ ) = $this->itemStack[$this->upIndex];
break;
-
case 'Top':
- list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
- $this->currentIndex) = $this->itemStack[0];
+ list(
+ $this->item,
+ $this->itemIterator,
+ $this->itemIteratorTotal,
+ /* dud */,
+ $this->upIndex,
+ $this->currentIndex
+ ) = $this->itemStack[0];
break;
-
default:
$this->item = $this->getObj($name, $arguments, $cache, $cacheName);
$this->itemIterator = null;
@@ -145,14 +221,14 @@ class SSViewer_Scope
break;
}
- $this->itemStack[] = array(
+ $this->itemStack[] = [
$this->item,
$this->itemIterator,
$this->itemIteratorTotal,
null,
$this->upIndex,
$this->currentIndex
- );
+ ];
return $this;
}
@@ -169,6 +245,11 @@ class SSViewer_Scope
return $result;
}
+ /**
+ * Jump to the last item in the stack, called when a new item is added before a loop/with
+ *
+ * @return self
+ */
public function pushScope()
{
$newLocalIndex = count($this->itemStack) - 1;
@@ -183,6 +264,11 @@ class SSViewer_Scope
return $this;
}
+ /**
+ * Jump back to "previous" item in the stack, called after a loop/with block
+ *
+ * @return self
+ */
public function popScope()
{
$this->localIndex = $this->popIndex;
@@ -191,6 +277,11 @@ class SSViewer_Scope
return $this;
}
+ /**
+ * Fast-forwards the current iterator to the next item
+ *
+ * @return mixed
+ */
public function next()
{
if (!$this->item) {
@@ -205,7 +296,7 @@ class SSViewer_Scope
}
$this->itemStack[$this->localIndex][SSViewer_Scope::ITEM_ITERATOR] = $this->itemIterator;
- $this->itemIteratorTotal = iterator_count($this->itemIterator); //count the total number of items
+ $this->itemIteratorTotal = iterator_count($this->itemIterator); // Count the total number of items
$this->itemStack[$this->localIndex][SSViewer_Scope::ITEM_ITERATOR_TOTAL] = $this->itemIteratorTotal;
$this->itemIterator->rewind();
} else {
@@ -217,13 +308,19 @@ class SSViewer_Scope
if (!$this->itemIterator->valid()) {
return false;
}
+
return $this->itemIterator->key();
}
+ /**
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ */
public function __call($name, $arguments)
{
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
- $retval = $on ? call_user_func_array(array($on, $name), $arguments) : null;
+ $retval = $on ? $on->$name(...$arguments) : null;
$this->resetLocalScope();
return $retval;