Skip to content

Commit

Permalink
FIX Don't use ArrayIterator for templates
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli committed May 22, 2024
1 parent 6ce976a commit edc33c4
Show file tree
Hide file tree
Showing 8 changed files with 16 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/ORM/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function exists()
public function getIterator(): Traversable
{
foreach ($this->items as $i => $item) {
if (is_array($item)) {
if (is_array($item) && !array_is_list($item)) {
yield new ArrayData($item);
} else {
yield $item;
Expand Down
2 changes: 0 additions & 2 deletions src/View/SSTemplateParser.peg
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,6 @@ class SSTemplateParser extends Parser implements TemplateParser
if (isset($sub['Call']['CallArguments']) && isset($sub['Call']['CallArguments']['php'])) {
$arguments = $sub['Call']['CallArguments']['php'];
$res['php'] .= "->$method('$property', [$arguments], true)";
} elseif ($property === 'Count') {
$res['php'] .= "->$property()";
} else {
$res['php'] .= "->$method('$property', null, true)";
}
Expand Down
9 changes: 1 addition & 8 deletions src/View/SSTemplateParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -778,8 +778,6 @@ function Lookup_AddLookupStep(&$res, $sub, $method)
if (isset($sub['Call']['CallArguments']) && isset($sub['Call']['CallArguments']['php'])) {
$arguments = $sub['Call']['CallArguments']['php'];
$res['php'] .= "->$method('$property', [$arguments], true)";
} elseif ($property === 'Count') {
$res['php'] .= "->$property()";
} else {
$res['php'] .= "->$method('$property', null, true)";
}
Expand Down Expand Up @@ -1888,8 +1886,6 @@ function PresenceCheck_Argument(&$res, $sub)
$res['php'] .= '((bool)'.$sub['php'].')';
} else {
$php = ($sub['ArgumentMode'] == 'default' ? $sub['lookup_php'] : $sub['php']);
// TODO: kinda hacky - maybe we need a way to pass state down the parse chain so
// Lookup_LastLookupStep and Argument_BareWord can produce hasValue instead of XML_val
$res['php'] .= str_replace('$$FINAL', 'hasValue', $php ?? '');
}
}
Expand Down Expand Up @@ -5294,8 +5290,6 @@ function Text__finalise(&$res)
$text = stripslashes($text ?? '');
$text = addcslashes($text ?? '', '\'\\');

// TODO: This is pretty ugly & gets applied on all files not just html. I wonder if we can make this
// non-dynamically calculated
$code = <<<'EOC'
(\SilverStripe\View\SSViewer::getRewriteHashLinksDefault()
? \SilverStripe\Core\Convert::raw2att( preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI'] ) )
Expand Down Expand Up @@ -5334,8 +5328,7 @@ public function compileString($string, $templateName = "", $includeDebuggingComm

$this->includeDebuggingComments = $includeDebuggingComments;

// Ignore UTF8 BOM at beginning of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
// (and other encodings) properly
// Ignore UTF8 BOM at beginning of string.
if (substr($string ?? '', 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
$this->pos = 3;
}
Expand Down
32 changes: 1 addition & 31 deletions src/View/SSViewer_DataPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -308,29 +308,6 @@ public function getObj($name, $arguments = [], $cache = false, $cacheName = null
*/
public function __call($name, $arguments)
{
// $Count should be handled specially, so we can count raw arrays and iterables
// which aren't ViewableData.
if (empty($arguments) && ($name === 'Count' || $name === 'count')) {
$item = $this->getItem();
$result = null;
if ($item instanceof ViewableData) {
// Respect ViewableData casting
$result = $item->XML_val($name, [], true);
} elseif (is_object($item)) {
// Get the method or property from objects, if there is one
if (ClassInfo::hasMethod($item, $name)) {
$result = $item->$name();
} elseif (isset($item->$name)) {
$result = $item->$name;
}
} elseif (is_countable($item)) {
// Count countables
$result = count($item);
}
$this->resetLocalScope();
return $result;
}

// Extract the method name and parameters
$property = $arguments[0]; // The name of the public function being called

Expand All @@ -341,14 +318,7 @@ public function __call($name, $arguments)
if ($val) {
$obj = $val['obj'];
if ($name === 'hasValue') {
// Check if a value exists, e.g. <% if $obj %>
if ($obj instanceof ViewableData) {
$result = $obj->exists();
} elseif (is_countable($obj)) {
$result = count($obj) > 0;
} else {
$result = (bool) $obj;
}
$result = ($obj instanceof ViewableData) ? $obj->exists() : (bool)$obj;
} elseif (is_null($obj) || (is_scalar($obj) && !is_string($obj))) {
$result = $obj; // Nulls and non-string scalars don't need casting
} else {
Expand Down
10 changes: 6 additions & 4 deletions src/View/SSViewer_Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use ArrayIterator;
use Countable;
use Iterator;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\FieldType\DBBoolean;
use SilverStripe\ORM\FieldType\DBText;
use SilverStripe\ORM\FieldType\DBFloat;
Expand Down Expand Up @@ -130,16 +131,17 @@ public function getItem()
if (is_scalar($item)) {
$item = $this->convertScalarToDBField($item);
}
// Wrap arrays

// Wrap arrays in ViewableData so templates can handle them
if (is_array($item)) {
if (array_is_list($item)) {
// Wrap in ArrayIterator to respect method signature
$item = new ArrayIterator($item);
$item = ArrayList::create($item);
} else {
// Wrap in ArrayData so values can be accessed by key in templates
// Allow values to be accessed by key in templates
$item = ArrayData::create($item);
}
}

return $item;
}

Expand Down
11 changes: 4 additions & 7 deletions src/View/ViewableData.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use SilverStripe\Dev\Debug;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\View\SSViewer;
Expand Down Expand Up @@ -558,13 +559,12 @@ public function obj($fieldName, $arguments = [], $cache = false, $cacheName = nu
$value = $this->$fieldName;
}

// Wrap arrays
// Wrap arrays in ViewableData so templates can handle them
if (is_array($value)) {
if (array_is_list($value)) {
// Wrap in ArrayIterator to respect method signature
$value = new ArrayIterator($value);
$value = ArrayList::create($value);
} else {
// Wrap in ArrayData so values can be accessed by key in templates
// Allow values to be accessed by key in templates
$value = ArrayData::create($value);
}
}
Expand Down Expand Up @@ -615,9 +615,6 @@ public function hasValue($field, $arguments = [], $cache = true)
if ($result instanceof ViewableData) {
return $result->exists();
}
if (is_countable($result)) {
return count($result) > 0;
}
return (bool) $result;
}

Expand Down
4 changes: 0 additions & 4 deletions tests/php/View/SSViewerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1429,10 +1429,6 @@ public function provideCountIterable(): array
'iterable' => [1, 2, 3],
'inScope' => false,
],
'iterator' => [
'iterable' => new ArrayIterator([1, 2, 3]),
'inScope' => false,
],
'ArrayList' => [
'iterable' => new ArrayList([['Val' => 1], ['Val' => 2], ['Val' => 3]]),
'inScope' => false,
Expand Down
6 changes: 3 additions & 3 deletions tests/php/View/ViewableDataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace SilverStripe\View\Tests;

use ArrayIterator;
use ReflectionMethod;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\ArrayList;
use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer;
use SilverStripe\View\Tests\ViewableDataTest\ViewableDataTestExtension;
Expand Down Expand Up @@ -285,14 +285,14 @@ public function provideWrapArrayInObj(): array
return [
'empty array' => [
'arr' => [],
'expectedClass' => ArrayIterator::class,
'expectedClass' => ArrayList::class,
],
'fully indexed array' => [
'arr' => [
'value1',
'value2',
],
'expectedClass' => ArrayIterator::class,
'expectedClass' => ArrayList::class,
],
'fully associative array' => [
'arr' => [
Expand Down

0 comments on commit edc33c4

Please sign in to comment.