- Issue created by @miha.wagner
- Merge request !39#3498774 Explicitly cast $value to string in StripTagsFilter plugin. → (Open) created by miha.wagner
- 🇺🇸United States tr Cascadia
OK. Can you add a few asserts to the DataFilterTest to test this use case? Right now it just tests passing a literal string. This should be enhanced to check a Stringable object.
- 🇸🇮Slovenia miha.wagner
I've added a test case to test with a $value of FilteredMarkup. Though this begs the question, if the value is being cast to a string via an explicit cast, maybe it would also make sense to check if the value implements __toString() or implements \Stringable in the canFilter method?
- 🇺🇸United States tr Cascadia
Thanks for the test. As expected, the new test passes with your changes, and also as expected the test-only (without the other changes) fails (see the test output). That's a good sign.
Note: you didn't use the
$htmlString
variable you defined...Yes, I intended that
canFilter()
should be changed, if necessary. ButcanFilter()
is checking on the core Drupal data type via theDataDefinition
, not on the PHP data type. I think the core 'string' data type should be sufficient here.But one thing about the test:
$data = $this->typedDataManager->create(DataDefinition::create('string'), FilteredMarkup::create('<b>Test <em>striptags</em> filter</b>')); $this->assertEquals('Test striptags filter', $filter->filter($data->getDataDefinition(), $data->getValue(), []))
Looking at this and the other data filter tests makes me question a few things about how all the data filters are implemented. Your original issue seems to indicate that there's a bigger problem here that needs more than just a type cast to fix.
In this case, I would have expected that
$data
is of the 'string' data type, and that when initialized with aFilteredMarkup
, there would be some internal casting or coercion to change theFilteredMarkup
into a string data type. But that doesn't seem to be the case because$data->getValue()
returns aFilteredMarkup
instead of a string. That's part of the core string DataType, and suggests to me maybe we should be using$data->getCastedValue()
in all these data filters AND inPlaceholderResolver
rather than$data->getValue()
.In all the tests,
filter()
is called with$data->getDataDefinition()
and$data->getValue()
, but there is nothing that prevents the caller passing two parameters that have nothing to do with each other, like$first_data->getDataDefinition()
and$second_data->getValue()
. The only placefilter()
is called is insidePlaceholderResolver
, where these two values are always taken from the same variable$data
, but that's not guaranteed iffilter()
is called from any other place or any other module.Likewise,
filtersTo()
is returning a 'string'DataDefinition
object. So what I would expectfilter()
to return is a TypedData object of that type. But insteadfilter()
in this case is returning a simple PHP string. But that's probably best left for a different issue, because changing the return type offilter()
could have a lot of consequences.So the above patch is doing a cast inside of
filter($definition, $value)
, which is making the assumptions that:
1)$definition
is of type 'string'
2)$value
can be cast to a 'string'Instead, I think ALL the data filters should be doing something like this:
public function filter(DataDefinitionInterface $definition, $value, array $arguments, ?BubbleableMetadata $bubbleable_metadata = NULL) { $data = $this->typedDataManager->create($definition, $value) ; return strip_tags($data->getCastedValue()); }
You see what I am doing? Instead of assuming that
$definition
and$value
are related to each other, I force$value
to be treated like$definition
. Do you think this makes sense? - 🇸🇮Slovenia miha.wagner
Yes that makes sense. I've gone ahead and changed that. If the other filters will also be doing it like this then I thought it
made sense to do the preparing of the value in the base class. What do you think about that change?