EBernhardson has uploaded a new change for review. https://gerrit.wikimedia.org/r/119920
Change subject: Revert "Auto-wrapping explicit escaper for templates" ...................................................................... Revert "Auto-wrapping explicit escaper for templates" This reverts commit c90c7080d8004e5001e5ef98aea8ebe4d5573748. Instead we will be converting to lightncandy, a handlebars implementation in php. Change-Id: I122190ef09c26d443c6f5152c9cc09439d5ab689 --- M Flow.php M container.php M includes/Exception/ExceptionHandling.php D includes/Template.php D includes/Template/ErrorHelper.php D includes/Template/Escaper.php D includes/Template/OutputString.php D tests/EscaperTest.php D tests/OutputStringTest.php D tests/TemplateTest.php 10 files changed, 0 insertions(+), 803 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow refs/changes/20/119920/1 diff --git a/Flow.php b/Flow.php index 825a292..d9a2874 100755 --- a/Flow.php +++ b/Flow.php @@ -159,14 +159,6 @@ $wgAutoloadClasses['Flow\Data\OneStepUserNameQuery'] = $dir . 'includes/Data/OneStepUserNameQuery.php'; $wgAutoloadClasses['Flow\Data\TwoStepUserNameQuery'] = $dir . 'includes/Data/TwoStepUserNameQuery.php'; -// Templating -$wgAutoloadClasses['Flow\Template'] = $dir . 'includes/Template.php'; -$wgAutoloadClasses['Flow\Template\Escaper'] = $dir . 'includes/Template/Escaper.php'; -$wgAutoloadClasses['Flow\Template\OutputString'] = $dir . 'includes/Template/OutputString.php'; -$wgAutoloadClasses['Flow\Template\TextString'] = $dir . 'includes/Template/OutputString.php'; -$wgAutoloadClasses['Flow\Template\HtmlString'] = $dir . 'includes/Template/OutputString.php'; -$wgAutoloadClasses['Flow\Template\ErrorHelper'] = $dir . 'includes/Template/ErrorHelper.php'; - // database interaction for singular models $wgAutoloadClasses['Flow\Data\RevisionStorage'] = $dir . 'includes/Data/RevisionStorage.php'; $wgAutoloadClasses['Flow\Data\PostRevisionStorage'] = $dir . 'includes/Data/PostRevisionStorage.php'; @@ -239,7 +231,6 @@ $wgAutoloadClasses['Flow\Exception\DataPersistenceException'] = $dir . 'includes/Exception/ExceptionHandling.php'; $wgAutoloadClasses['Flow\Exception\WikitextException'] = $dir . 'includes/Exception/ExceptionHandling.php'; $wgAutoloadClasses['Flow\Exception\NoIndexException'] = $dir . 'includes/Exception/ExceptionHandling.php'; -$wgAutoloadClasses['Flow\Exception\RuntimeException'] = $dir . 'includes/Exception/ExceptionHandling.php'; // Configuration diff --git a/container.php b/container.php index cfe73fb..7370072 100644 --- a/container.php +++ b/container.php @@ -40,26 +40,6 @@ } ); // Templating - -// array of closures to delay instantiation until use of -// individual helpers. -$c['template.helpers'] = array( - // Backward Compatability for old templating helper methods - 'bc' => function() use ( $c ) { return $c['templating']; }, - // Consistent rendering of error messages - 'errors' => function() { return new Flow\Template\ErrorHelper; }, -); - -// not shared, new template every time -$c['template'] = function( $c ) { - return new Flow\Template( - $c['templating.namespaces'], - $c['template.helpers'], - $c['templating.global_variables'], - $c['output'] - ); -}; - // (this implementation is mostly useless actually) $c['url_generator'] = $c->share( function( $c ) { return new Flow\UrlGenerator( diff --git a/includes/Exception/ExceptionHandling.php b/includes/Exception/ExceptionHandling.php index fe7d595..18ebd05 100644 --- a/includes/Exception/ExceptionHandling.php +++ b/includes/Exception/ExceptionHandling.php @@ -249,9 +249,3 @@ return array ( 'no-index' ); } } - -class RuntimeException extends FlowException { - protected function getErrorCodeList() { - return array( 'other' ); - } -} diff --git a/includes/Template.php b/includes/Template.php deleted file mode 100644 index 1ff96f6..0000000 --- a/includes/Template.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php - -namespace Flow; - -use Flow\Exception\RuntimeException; -use OutputPage; - -/** - * Provides a method for rendering a template implemented in raw php. - * Values passed into the template are accessed through $this->varname - * within the template file. - * - * A map of prefix's to directories can be provided in the constructor - * as the $namespaces parameter. This are then used when calling render - * or partial to provide the correct directory - * - * $template->render( 'flow:post.html.php' ); - * - * Values passed to render or partial to be included in the template are - * accessed through property access on $this within the template. All values - * in the template are explicitly escaped. You must call either ->sanitized() - * or ->__raw() on the value. Also currently providing text/escaped/parse - * methods so the strings can be used in place of a wfMessage call. - * - * <?php echo $this->username->escaped() ?> - * - * The escaper is recursive. When wraping an object all methods have - * their return value escaped, all property access is escaped. - * - * <?php echo $this->post->getCreatorId()->escaped() ?> - * <?php echo $this->post->creatorId->escaped() ?> - * - * Helper methods can be added via the constructor. Helpers must be - * callable. It is suggested to lazy load objects instead of creating - * every possible helper ahead of time. The \Pimple::share method will - * ensure the inner callback is only called a single time, and the object - * will only be created when needed. - * - * $helpers = array( 'error' => \Pimple::share( function() { return new ErrorHelper; } ) ); - * $template = new Template( array(), $helpers ); - * - * Helpers are called for all undefined method calls. Helper return values - * receive the same recursive escaping treatment as passed variables. - * - * <?php echo $this->error()->block( $block )->escaped() ?> - * - * Due to how arrays work in php array keys cannot be escaped, but - * echoing a value without the ->escaped() or ->sanitized() call is - * obvious to a reviewer. Array values receive the standard recursive - * escaping. The proper way to output an array key within a template is: - * - * <?php foreach( $this->posts as $id => $post ): ?> - * ... - * <?php echo $this->escape( $id )->escaped() ?> - * ... - * <?php endforeach ?> - * - * This wrapping makes some things more difficult, like passing a string - * into a template to be included in a wfMessage call. A __raw() method is - * provided to unwrap the escaped values. - * - * <?php echo wfMessage( 'flow-reply-link', $this->username->__raw() )->escaped() ?> - * - */ - -class Template { - - /** - * @var string[] Map from prefix to path - */ - protected $namespaces; - - /** - * @var callable[] Map from name of helper to callable that returns it - */ - protected $helpers; - - /** - * @var array The raw unescaped template data - */ - protected $data; - - /** - * @var OutputPage|null - */ - protected $output; - - /** - * @param array $namespaces Map from namespace prefix to a directory - * @param array $helpers Map from helper name to callable returning the helper - * @param array $data The dynamic data used in the template - * @param OutputPage $output - */ - public function __construct( array $namespaces = array(), array $helpers = array(), array $data = array(), OutputPage $output = null ) { - $this->namespaces = $namespaces; - $this->helpers = $helpers; - $this->setData( $data ); - $this->output = $output; - } - - /** - * @param string $prop - * @return mixed - */ - public function __get( $prop ) { - return $this->escape( $this->data[$prop] ); - } - - /** - * @param string $prop - * @param mixed $value - */ - public function __set( $prop, $value ) { - $this->data[$prop] = $value; - } - - /** - * @param string $prop - * @return boolean - */ - public function __isset( $prop ) { - return isset( $this->data[$prop] ); - } - - /** - * @param string $prop - */ - public function __unset( $prop ) { - unset( $this->data[$prop] ); - } - - /** - * Call a helper method - * - * @param string $name Name of the helper - * @param array $args Arguments to pass to the helper - * @return mixed - * @throws RuntimeException When an unknown helper is requested - */ - public function __call( $name, $args ) { - if ( !isset( $this->helpers[$name] ) ) { - throw new RuntimeException( 'Unknown template helper: ' . $name, 'other' ); - } - return $this->escape( call_user_func_array( $this->helpers[$name], $args ) ); - } - - /** - * @return array - */ - public function __raw() { - return $this->data; - } - - public function escape( $value ) { - return Template\Escaper::__escape( $value ); - } - - /** - * @param array $data - * @return Template - */ - public function addData( array $data ) { - return $this->setData( $data + $this->data ); - } - - /** - * @param array $data - * @return Template - */ - public function setData( array $data ) { - $this->data = $data; - return $this; - } - - /** - * @param string $file optionally prefixed with namespace - * @return string full path to the requested file - * @throws RuntimeException When prefixed namespace does not exist - */ - public function findFile( $file ) { - if ( false === strpos( $file, ':' ) ) { - return $file; - } - list( $ns, $file ) = explode( ':', $file, 2 ); - if ( !isset( $this->namespaces[$ns] ) ) { - throw new RuntimeException( 'Unknown template namespace', 'other' ); - } - - return rtrim( $this->namespaces[$ns], '/' ) . '/' . ltrim( $file, '/' ); - } - - /** - * @param string $name - * @param array $data - * @param boolean $returnString - * @return string - */ - public function render( $name, array $data = null, $returnString = true ) { - if ( $data !== null ) { - $this->addData( $data ); - } - $output = $this->renderInternal( $this->findFile( $name ) ); - if ( $returnString === false ) { - $this->output->addHTML( $output ); - return ''; - } else { - return $output; - } - } - - /** - * @param string $path - * @return string - */ - protected function renderInternal( $path ) { - ob_start(); - require $path; - return ob_get_clean(); - } - - /** - * @param string $name - * @param array $data - * @return string - */ - public function partial( $name, array $data = null ) { - $partial = clone $this; - if ( $data !== null ) { - $partial->setData( $data ); - } - return $partial->render( $name, null, true ); - } - -} - diff --git a/includes/Template/ErrorHelper.php b/includes/Template/ErrorHelper.php deleted file mode 100644 index a044470..0000000 --- a/includes/Template/ErrorHelper.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -namespace Flow\Template; - -class ErrorHelper { - - /** - * Generic error output for block errors - * - * @param \Flow\Block\Block $block - * @return HtmlString - */ - public function block( $block ) { - if ( $block instanceof Escaper ) { - $block = $block->__raw(); - } - if ( !$block->hasErrors() ) { - return ''; - } - // This needs to use parse, some use wikitext such as - // https://www.mediawiki.org/wiki/MediaWiki:Abusefilter-disallowed - $errors = array(); - foreach ( $block->getErrors() as $error ) { - $errors[] = $block->getErrorMessage( $error )->parse(); - } - return new HtmlString( '<ul><li>' . implode( '</li><li>', $errors ) . '</li></ul>' ); - } -} diff --git a/includes/Template/Escaper.php b/includes/Template/Escaper.php deleted file mode 100644 index 14e4d32..0000000 --- a/includes/Template/Escaper.php +++ /dev/null @@ -1,172 +0,0 @@ -<?php - -namespace Flow\Template; - -use ArrayAccess; -use ArrayIterator; -use IteratorAggregate; -use Message; -use Flow\Exception\RuntimeException; - -/** - * This class wraps all values that are unsafe for output to the user - * with the OutputString interface. The core Message class doesn't - * explicitly implement OutputString, but it has all the correct methods. - * - * If the value to be escaped is a complex data type(object|array) this - * will return an Escaper instance wrapping that complex data type. All - * php magic methods are implemented such that the escaped object can be - * used the same as if it were unescaped, with the only difference being - * that all properties and method return values pass through the escaping - * function. - * - * NOTE: Array keys cannot be wrapped in an OutputString object. Any - * array key to be output must be explicitly escaped. - * NOTE: Most array functions, like array_keys will not work. Usage of - * arrays in templates is limited to foreach and array access - * - * Usage: - * $escaped = Flow\Template\Escaper::__escape( $anything ); - */ -class Escaper implements ArrayAccess, IteratorAggregate { - /** - * @var object The object being escaped - */ - protected $object; - - /** - * @param object $object The object to escape - */ - public function __construct( $object ) { - $this->object = $object; - } - - /** - * @param string $prop - * @return mixed - */ - public function __get( $prop ) { - return self::__escape( $this->object->$prop ); - } - - /** - * @param string $prop - * @param mixed $value - */ - public function __set( $prop, $value ) { - $this->object->$prop = $value; - } - - /** - * @param string $prop - * @return boolean - */ - public function __isset( $prop ) { - return isset( $this->object->$prop ); - } - - /** - * @param string $prop - */ - public function __unset( $prop ) { - unset( $this->object->$prop ); - } - - /** - * @param string $method - * @param array $args - * @return mixed - */ - public function __call( $method, $args ) { - return self::__escape( call_user_func_array( array( $this->object, $method ), $args ) ); - } - - - /** - * @param string $offset - * @return boolean - */ - public function offsetExists( $offset ) { - return isset( $this->object[$offset] ); - } - - /** - * @param string $offset - * @return mixed - */ - public function offsetGet( $offset ) { - return self::__escape( $this->object[$offset] ); - } - - /** - * @param string $offset - * @param mixed $value - */ - public function offsetSet( $offset, $value ) { - $this->object[$offset] = $value; - } - - /** - * @param string $offset - */ - public function offsetUnset( $offset ) { - unset( $this->object[$offset] ); - } - - /** - * Return the unescaped object being escaped. Note that if an array - * was passed in an ArrayObject comes back out through this method. - * This method gets a double underscore prefix to avoid conflicting - * with methods in the wrapped object. - * - * @return object - */ - public function __raw() { - return $this->object; - } - - /** - * Returns an escaped value. This method gets a double underscore - * prefix to avoid conflicting with methods in the wrapped object. - * - * NOTE: Array keys are not handled, if used as part of the output - * they must be independantly escaped. - * NOTE: Although Message doesn't implement OutputString, it matches - * the interface. - * - * @param mixed $value The value to escape. If the value is already - * an OutputString or Message instance then it is already escaped - * and returned as is. If the value is an array or an object it - * is wrapped with this Escaper object. Booleans and nulls pass through - * unchanged. Everything else is wrapped in the TextString class - * which implements OutputString to properly escape the values. - * - * @return Escaper|OutputString|Message - */ - static public function __escape( $value ) { - if ( $value instanceof OutputString || $value instanceof Message ) { - return $value; - } - - // arrays must be wrapped, ensuring that even values added to the - // array after escaping receive sanitization. - if ( is_object( $value ) || is_array( $value ) ) { - return new self( $value ); - } - - // don't cast null to string otherwise object|null - // becomes object|string when using __raw - if ( is_null( $value ) ) { - return new TextString( $value ); - } - - return new TextString( (string)$value ); - } - - public function getIterator() { - if ( !is_array( $this->object ) ) { - throw new RuntimeException( 'Can only currently iterate arrays' ); - } - return new ArrayIterator( array_map( array( 'Flow\Template\Escaper', '__escape' ), $this->object ) ); - } -} diff --git a/includes/Template/OutputString.php b/includes/Template/OutputString.php deleted file mode 100644 index afe3577..0000000 --- a/includes/Template/OutputString.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -namespace Flow\Template; - -/** - * Wraps strings with an api providing the same escaping - * expectations as Message - */ -abstract class OutputString { - /** - * @var string - */ - protected $string; - - /** - * @param string $string - */ - public function __construct( $string ) { - $this->string = $string; - } - - /** - * @return string Enclosed string sanitized for HTML output - */ - abstract function sanitized(); - - /** - * @return string Bare enclosed string - */ - public function __raw() { - return $this->string; - } - - /** - * only for api compat with Message - * @return string Bare enclosed string - */ - public function text() { - return $this->__raw(); - } - - /** - * only for api compat with Message - * @return string Enclosed string sanitized for HTML output - */ - public function escaped() { - return $this->sanitized(); - } - - /** - * only for api compat with Message - * @return string Enclosed string sanitized for HTML output - */ - public function parse() { - return $this->sanitized(); - } -} - -/** - * Wraps a plain text string that needs to be sanitized - */ -class TextString extends OutputString { - - /** - * @return string Enclosed string sanitized for HTML output - */ - public function sanitized() { - return htmlspecialchars( $this->string ); - } -} - -/** - * Wraps a pre-sanitized string of html - */ -class HtmlString extends OutputString { - - /** - * @param string $string A pre-sanitized string of html - * @param string $source Source of the html string, used only for debugging - */ - public function __construct( $string, $source = null ) { - parent::__construct( $string ); - $this->source = $source; - } - - /** - * @return string Bare enclosed string - */ - public function text() { - wfDebugLog( 'Flow', __METHOD__ . " :{$this->source}: Potential double escape" ); - return $this->string; - } - - /** - * @return string Enclosed string sanitized for HTML output - */ - public function sanitized() { - return (string)$this->string; - } -} - diff --git a/tests/EscaperTest.php b/tests/EscaperTest.php deleted file mode 100644 index cb634ca..0000000 --- a/tests/EscaperTest.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -namespace Flow\Tests; - -use Flow\Template\Escaper; -use Flow\Template\TextString; - -class EscaperTest extends \MediaWikiTestCase { - - public function testGetSetIssetUnset() { - $escaped = Escaper::__escape( (object)array() ); - $this->assertFalse( isset( $escaped->someproperty ) ); - $escaped->someproperty = 'naismith'; - $this->assertTrue( isset( $escaped->someproperty ) ); - $this->assertEquals( new TextString( 'naismith' ), $escaped->someproperty ); - unset( $escaped->someproperty ); - $this->assertFalse( isset( $escaped->someproperty ) ); - - try { - $escaped->someproperty; - $this->fail( 'someproperty must not exist' ); - } catch ( \PHPUnit_Framework_Error_Notice $e ) { - $this->assertEquals( 'Undefined property: stdClass::$someproperty', $e->getMessage() ); - } - } - - public function testArrayAccess() { - $escaped = Escaper::__escape( array( - 'foo' => 'bar', - 'baz' => 'blimp', - ) ); - - $this->assertTrue( isset( $escaped['foo'] ) ); - $this->assertEquals( new TextString( 'bar' ), $escaped['foo'] ); - $this->assertEquals( new TextString( 'blimp'), $escaped['baz'] ); - unset( $escaped['foo'] ); - $this->assertFalse( isset( $escaped['foo'] ) ); - } - - public function testValueAddedToArrayIsEscaped() { - $escaped = Escaper::__escape( array() ); - $escaped['winner'] = 'chicken dinner'; - $this->assertTrue( isset( $escaped['winner'] ) ); - $this->assertEquals( new TextString( 'chicken dinner' ), $escaped['winner'] ); - } - - public function testArraysAreForeachable() { - $escaped = Escaper::__escape( array( 'bing' => 'bang' ) ); - $count = 0; - foreach ( $escaped as $key => $value ) { - $this->assertEquals( 'bing', $key ); - $this->assertEquals( new TextString( 'bang' ), $value ); - ++$count; - } - $this->assertEquals( 1, $count ); - } - - static public function escapedValueProvider() { - return array( - array( 'booleans true', true, '1' ), - array( 'booleans false', false, ''), - array( 'single quote', "'", "'" ), - array( 'double quote', '"', '"' ), - array( 'html tags', '<a>', '<a>' ), - array( 'null value', null, '' ), - ); - } - - /** - * @dataProvider escapedValueProvider - */ - public function testEscapedValue( $msg, $input, $output ) { - $this->assertEquals( $output, Escaper::__escape( $input )->sanitized(), $msg ); - } - - static public function rawValueProvider() { - $obj = new \stdClass; - $obj->foo = '42'; - - return array( - array( 'boolean true cast to string', true, '1' ), - array( 'boolean false cast to string', false, '' ), - array( 'null value passes through', null, null ), - array( 'object value passes through', $obj, $obj ), - ); - } - - /** - * @dataProvider rawValueProvider - */ - public function testRawValue( $msg, $input, $output ) { - $this->assertEquals( $output, Escaper::__escape( $input )->__raw(), $msg ); - } - -} diff --git a/tests/OutputStringTest.php b/tests/OutputStringTest.php deleted file mode 100644 index 6f05f36..0000000 --- a/tests/OutputStringTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -namespace Flow\Tests; - -use Flow\Template\OutputString; -use Flow\Template\TextString; -use Flow\Template\HtmlString; - -class OutputStringTest extends \MediaWikiTestCase { - - public function testTextStringWithPlainValue() { - $string = new TextString( 'zomg' ); - $this->assertEquals( 'zomg', $string->text() ); - $this->assertEquals( 'zomg', $string->escaped() ); - $this->assertEquals( 'zomg', $string->parse() ); - } - - public function testTextStringWithValueNeedingEscape() { - $val = '<script>alert( \'evil\' );</script>'; - $escaped = htmlspecialchars( $val ); - $string = new TextString( $val ); - $this->assertEquals( $val, $string->text() ); - $this->assertEquals( $escaped, $string->escaped() ); - $this->assertEquals( $escaped, $string->parse() ); - } - - public function testHtmlString() { - $string = new HtmlString( 'zomg' ); - $this->assertEquals( 'zomg', $string->text() ); - $this->assertEquals( 'zomg', $string->escaped() ); - $this->assertEquals( 'zomg', $string->parse() ); - } - - public function testHtmlStringPassesThrough() { - $val = '<script>alert( \'gumballs\' )</script>'; - $string = new HtmlString( $val ); - $this->assertEquals( $val, $string->escaped() ); - $this->assertEquals( $val, $string->parse() ); - } -} diff --git a/tests/TemplateTest.php b/tests/TemplateTest.php deleted file mode 100644 index 44263b3..0000000 --- a/tests/TemplateTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -namespace Flow\Tests; - -use Flow\Template; -use Flow\Template\TextString; - -class TemplateTest extends \MediaWikiTestCase { - - public function testGetSetIssetUnset() { - $template = new Template(); - $this->assertFalse( isset( $template->someproperty ) ); - $template->someproperty = 'foo'; - $this->assertTrue( isset( $template->someproperty ) ); - $this->assertEquals( new TextString( 'foo' ), $template->someproperty ); - unset( $template->someproperty ); - $this->assertFalse( isset( $template->someproperty ) ); - - try { - $template->someproperty; - $this->fail( 'someproperty must not exist' ); - } catch ( \PHPUnit_Framework_Error_Notice $e ) { - $this->assertEquals( 'Undefined index: someproperty', $e->getMessage() ); - } - } - - public function testGetHelper() { - $helpers = array( - 'something' => function() { - return 'helper'; - }, - ); - $template = new Template( array(), $helpers ); - $this->assertEquals( new TextString( 'helper' ), $template->something() ); - } - - public function testHelperWithArguments() { - $helpers = array( - 'otherthing' => function( $arg ) { - return $arg; - }, - ); - $template = new Template( array(), $helpers ); - $this->assertEquals( new TextString( 'myhelper' ), $template->otherthing( 'myhelper' ) ); - } - - public function testSetAndAddData() { - $template = new Template(); - $this->assertFalse( isset( $template->vor ) ); - $template->setData( array( 'vor' => 123, 'lord' => 456 ) ); - $this->assertTrue( isset( $template->vor ) ); - $this->assertEquals( new TextString( '123' ), $template->vor ); - $this->assertEquals( new TextString( '456' ), $template->lord ); - $template->addData( array( 'lord' => 789, 'miles' => 321 ) ); - $this->assertEquals( new TextString( '789' ), $template->lord ); - $this->assertEquals( new TextString( '321' ), $template->miles ); - } - - public function testUnconfiguredFindFile() { - $template = new Template(); - $this->assertEquals( - 'foo.html.php', - $template->findFile( 'foo.html.php' ), - 'With no namespace requested pass through the input string' - ); - } - - /** - * @expectedException Flow\Exception\RuntimeException - */ - public function testUnconfiguredNamespacedFindFile() { - $template = new Template(); - $template->findFile( 'foo:bar.html.php' ); - } - - public function testConfiguredNamespacedFindFile() { - $template = new Template( array( 'foo' => '/tmp', 'bar' => '/media' ) ); - $this->assertEquals( - '/tmp/bazinga.html.php', - $template->findFile( 'foo:bazinga.html.php' ) - ); - $this->assertEquals( - '/media/bazinga.html.php', - $template->findFile( 'bar:bazinga.html.php' ) - ); - } - - public function testRawDataIsAvailable() { - $data = array( 'orig' => 'data', 'is' => 'not <escaped>' ); - $template = new Template( array(), array(), $data ); - $this->assertEquals( $data, $template->__raw() ); - - $data += array( 'hi' => 'there' ); - $template->setData( $data ); - $this->assertEquals( $data, $template->__raw() ); - } -} -- To view, visit https://gerrit.wikimedia.org/r/119920 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I122190ef09c26d443c6f5152c9cc09439d5ab689 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: master Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits