From 2a17303601c85e3fdb657c8b7a19a39eb4bc92cc Mon Sep 17 00:00:00 2001 From: Possum Date: Wed, 26 Aug 2015 11:48:05 +0200 Subject: [PATCH] Mark test skipped if cannot write to cache directory. Add Traversable support for replace, sort and merge filters. --- doc/filters/merge.rst | 3 +- doc/filters/sort.rst | 3 +- lib/Twig/Extension/Core.php | 54 ++++++++++++++++--- test/Twig/Tests/Fixtures/filters/merge.test | 4 +- test/Twig/Tests/Fixtures/filters/replace.test | 8 ++- .../Fixtures/filters/replace_invalid_arg.test | 8 +++ test/Twig/Tests/Fixtures/filters/sort.test | 4 +- 7 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test diff --git a/doc/filters/merge.rst b/doc/filters/merge.rst index cb8b1b223..88780dd6f 100644 --- a/doc/filters/merge.rst +++ b/doc/filters/merge.rst @@ -42,6 +42,7 @@ overridden. .. note:: - Internally, Twig uses the PHP `array_merge`_ function. + Internally, Twig uses the PHP `array_merge`_ function. It supports + Traversable objects by transforming those to arrays. .. _`array_merge`: http://php.net/array_merge diff --git a/doc/filters/sort.rst b/doc/filters/sort.rst index 33311528f..350207f8e 100644 --- a/doc/filters/sort.rst +++ b/doc/filters/sort.rst @@ -12,6 +12,7 @@ The ``sort`` filter sorts an array: .. note:: Internally, Twig uses the PHP `asort`_ function to maintain index - association. + association. It supports Traversable objects by transforming + those to arrays. .. _`asort`: http://php.net/asort diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index cfffa8031..ee959ee9e 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -152,7 +152,7 @@ class Twig_Extension_Core extends Twig_Extension new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)), new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)), new Twig_SimpleFilter('format', 'sprintf'), - new Twig_SimpleFilter('replace', 'strtr'), + new Twig_SimpleFilter('replace', 'twig_replace_filter'), new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)), new Twig_SimpleFilter('abs', 'abs'), new Twig_SimpleFilter('round', 'twig_round'), @@ -549,6 +549,30 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu return $date; } +/** + * Replaces strings within a string. + * + * @param string $str String to replace in + * @param array|Traversable $from Replace values + * @param string|null $to Replace to, deprecated (@see http://php.net/manual/en/function.strtr.php) + * + * @return string + */ +function twig_replace_filter($str, $from, $to = null) +{ + if ($from instanceof Traversable) { + $from = iterator_to_array($from); + } elseif (is_string($from) && is_string($to)) { + @trigger_error('Using "replace" with character by character replacement is deprecated and will be removed in Twig 2.x', E_USER_DEPRECATED); + + return strtr($str, $from, $to); + } elseif (!is_array($from)) { + throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".',is_object($from) ? get_class($from) : gettype($from))); + } + + return strtr($str, $from); +} + /** * Rounds a number. * @@ -682,15 +706,23 @@ function _twig_markup2string(&$value) * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #} * * - * @param array $arr1 An array - * @param array $arr2 An array + * @param array|Traversable $arr1 An array + * @param array|Traversable $arr2 An array * * @return array The merged array */ function twig_array_merge($arr1, $arr2) { - if (!is_array($arr1) || !is_array($arr2)) { - throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or hashes; %s and %s given.', gettype($arr1), gettype($arr2))); + if ($arr1 instanceof Traversable) { + $arr1 = iterator_to_array($arr1); + } elseif (!is_array($arr1)) { + throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1))); + } + + if ($arr2 instanceof Traversable) { + $arr2 = iterator_to_array($arr2); + } elseif (!is_array($arr2)) { + throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', gettype($arr2))); } return array_merge($arr1, $arr2); @@ -874,7 +906,7 @@ function _twig_default_filter($value, $default = '') */ function twig_get_array_keys_filter($array) { - if (is_object($array) && $array instanceof Traversable) { + if ($array instanceof Traversable) { return array_keys(iterator_to_array($array)); } @@ -896,7 +928,7 @@ function twig_get_array_keys_filter($array) */ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false) { - if (is_object($item) && $item instanceof Traversable) { + if ($item instanceof Traversable) { return array_reverse(iterator_to_array($item), $preserveKeys); } @@ -928,12 +960,18 @@ function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false /** * Sorts an array. * - * @param array $array + * @param array|Traversable $array * * @return array */ function twig_sort_filter($array) { + if ($array instanceof Traversable) { + $array = iterator_to_array($array); + } elseif (!is_array($array)) { + throw new Twig_Error_Runtime(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', gettype($array))); + } + asort($array); return $array; diff --git a/test/Twig/Tests/Fixtures/filters/merge.test b/test/Twig/Tests/Fixtures/filters/merge.test index 2bd3d4c04..81371a41b 100644 --- a/test/Twig/Tests/Fixtures/filters/merge.test +++ b/test/Twig/Tests/Fixtures/filters/merge.test @@ -6,11 +6,13 @@ {{ {'bar': 'foo'}|merge(items)|join }} {{ {'bar': 'foo'}|merge(items)|keys|join }} {{ numerics|merge([4, 5, 6])|join }} +{{ traversable.a|merge(traversable.b)|join }} --DATA-- -return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3)) +return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3), 'traversable' => array('a' => new ArrayObject(array(0 => 1, 1 => 2, 2 => 3)), 'b' => new ArrayObject(array('a' => 'b')))) --EXPECT-- barfoo foobar foobar barfoo 123456 +123b diff --git a/test/Twig/Tests/Fixtures/filters/replace.test b/test/Twig/Tests/Fixtures/filters/replace.test index 4021660be..06be7e270 100644 --- a/test/Twig/Tests/Fixtures/filters/replace.test +++ b/test/Twig/Tests/Fixtures/filters/replace.test @@ -1,8 +1,12 @@ --TEST-- "replace" filter --TEMPLATE-- -{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }} +{{ "I liké %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }} +{{ 'I like single replace operation only %that%'|replace({'%that%' : '%that%1'}) }} +{{ 'I like %this% and %that%.'|replace(traversable) }} --DATA-- -return array() +return array('traversable' => new ArrayObject(array('%this%' => 'foo', '%that%' => 'bar'))) --EXPECT-- +I liké foo and bar. +I like single replace operation only %that%1 I like foo and bar. diff --git a/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test b/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test new file mode 100644 index 000000000..08ecfd490 --- /dev/null +++ b/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test @@ -0,0 +1,8 @@ +--TEST-- +Exception for invalid argument type in replace call +--TEMPLATE-- +{{ 'test %foo%'|replace(stdClass) }} +--DATA-- +return array('stdClass' => new \stdClass()) +--EXCEPTION-- +Twig_Error_Runtime: The "replace" filter expects an array or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2. diff --git a/test/Twig/Tests/Fixtures/filters/sort.test b/test/Twig/Tests/Fixtures/filters/sort.test index 21d575f18..c67c18ea9 100644 --- a/test/Twig/Tests/Fixtures/filters/sort.test +++ b/test/Twig/Tests/Fixtures/filters/sort.test @@ -3,8 +3,10 @@ --TEMPLATE-- {{ array1|sort|join }} {{ array2|sort|join }} +{{ traversable|sort|join }} --DATA-- -return array('array1' => array(4, 1), 'array2' => array('foo', 'bar')) +return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'), 'traversable' => new ArrayObject(array(0 => 3, 1 => 2, 2 => 1))) --EXPECT-- 14 barfoo +123