Commit 6bad8672 authored by alexpott's avatar alexpott

Issue #2853823 by Jo Fitzgerald, claudiu.cristea, Munavijayalakshmi,...

Issue #2853823 by Jo Fitzgerald, claudiu.cristea, Munavijayalakshmi, phenaproxima, alexpott, heddn: Explode processor is too strict regarding to the input value
parent 45d83369
......@@ -22,6 +22,11 @@
* returned.
* - If the limit parameter is zero, then this is treated as 1.
* - delimiter: The boundary string.
* - strict: (optional) When this boolean is TRUE, the source should be strictly
* a string. If FALSE is passed, the source value is casted to a string before
* being split. Also, in this case, the values casting to empty strings are
* converted to empty arrays, instead of an array with a single empty string
* item ['']. Defaults to TRUE.
*
* Example:
*
......@@ -29,8 +34,8 @@
* process:
* bar:
* plugin: explode
* source: foo
* delimiter: /
* source: foo
* delimiter: /
* @endcode
*
* If foo is "node/1", then bar will be ['node', '1']. The PHP equivalent of
......@@ -44,9 +49,9 @@
* process:
* bar:
* plugin: explode
* source: foo
* limit: 1
* delimiter: /
* source: foo
* limit: 1
* delimiter: /
* @endcode
*
* If foo is "node/1/edit", then bar will be ['node', '1/edit']. The PHP
......@@ -56,6 +61,30 @@
* $bar = explode('/', $foo, 1);
* @endcode
*
* If the 'strict' configuration is set to FALSE, the input value is casted to a
* string before being spilt:
*
* @code
* process:
* bar:
* plugin: explode
* source: foo
* delimiter: /
* strict: false
* @endcode
*
* If foo is 123 (as integer), then bar will be ['123']. If foo is TRUE, then
* bar will be ['1']. The PHP equivalent of this would be:
*
* @code
* $bar = explode('/', (string) 123);
* $bar = explode('/', (string) TRUE);
* @endcode
*
* If the 'strict' configuration is set to FALSE, the source value casting to
* an empty string are converted to an empty array. For example, with the last
* configuration, if foo is '', NULL or FALSE, then bar will be [].
*
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
*
* @MigrateProcessPlugin(
......@@ -68,18 +97,29 @@ class Explode extends ProcessPluginBase {
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (is_string($value)) {
if (!empty($this->configuration['delimiter'])) {
$limit = isset($this->configuration['limit']) ? $this->configuration['limit'] : PHP_INT_MAX;
return explode($this->configuration['delimiter'], $value, $limit);
}
else {
throw new MigrateException('delimiter is empty');
}
if (empty($this->configuration['delimiter'])) {
throw new MigrateException('delimiter is empty');
}
else {
$strict = array_key_exists('strict', $this->configuration) ? $this->configuration['strict'] : TRUE;
if ($strict && !is_string($value)) {
throw new MigrateException(sprintf('%s is not a string', var_export($value, TRUE)));
}
elseif (!$strict) {
// Check if the incoming value can cast to a string.
$original = $value;
if (!is_string($original) && ($original != ($value = @strval($value)))) {
throw new MigrateException(sprintf('%s cannot be casted to a string', var_export($original, TRUE)));
}
// Empty strings should be exploded to empty arrays.
if ($value === '') {
return [];
}
}
$limit = isset($this->configuration['limit']) ? $this->configuration['limit'] : PHP_INT_MAX;
return explode($this->configuration['delimiter'], $value, $limit);
}
/**
......
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\migrate\Unit\process;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\migrate\process\Explode;
use Drupal\migrate\Plugin\migrate\process\Concat;
......@@ -53,23 +54,65 @@ public function testChainedTransform() {
/**
* Test explode fails properly on non-strings.
*
* @expectedException \Drupal\migrate\MigrateException
*
* @expectedExceptionMessage is not a string
*/
public function testExplodeWithNonString() {
$this->setExpectedException(MigrateException::class, 'is not a string');
$this->plugin->transform(['foo'], $this->migrateExecutable, $this->row, 'destinationproperty');
}
/**
* Test explode fails with empty delimiter.
* Tests that explode works on non-strings but with strict set to FALSE.
*
* @expectedException \Drupal\migrate\MigrateException
*
* @expectedExceptionMessage delimiter is empty
* @dataProvider providerExplodeWithNonStrictAndEmptySource
*/
public function testExplodeWithNonStrictAndEmptySource($value, $expected) {
$plugin = new Explode(['delimiter' => '|', 'strict' => FALSE], 'map', []);
$processed = $plugin->transform($value, $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame($expected, $processed);
}
/**
* Data provider for ::testExplodeWithNonStrictAndEmptySource().
*/
public function providerExplodeWithNonStrictAndEmptySource() {
return [
'normal_string' => ['a|b|c', ['a', 'b', 'c']],
'integer_cast_to_string' => [123, ['123']],
'zero_integer_cast_to_string' => [0, ['0']],
'true_cast_to_string' => [TRUE, ['1']],
'null_empty_array' => [NULL, []],
'false_empty_array' => [FALSE, []],
'empty_string_empty_array' => ['', []],
];
}
/**
* Tests that explode raises an exception when the value cannot be casted to
* string.
*/
public function testExplodeWithNonStrictAndNonCastable() {
$plugin = new Explode(['delimiter' => '|', 'strict' => FALSE], 'map', []);
$this->setExpectedException(MigrateException::class, 'cannot be casted to a string');
$processed = $plugin->transform(['foo'], $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame(['foo'], $processed);
}
/**
* Tests that explode with an empty string and strict check returns a
* non-empty array.
*/
public function testExplodeWithStrictAndEmptyString() {
$plugin = new Explode(['delimiter' => '|'], 'map', []);
$processed = $plugin->transform('', $this->migrateExecutable, $this->row, 'destinationproperty');
$this->assertSame([''], $processed);
}
/**
* Test explode fails with empty delimiter.
*/
public function testExplodeWithEmptyDelimiter() {
$this->setExpectedException(MigrateException::class, 'delimiter is empty');
$plugin = new Explode(['delimiter' => ''], 'map', []);
$plugin->transform('foo,bar', $this->migrateExecutable, $this->row, 'destinationproperty');
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment