summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2017-03-17 15:49:43 (GMT)
committerAlex Pott2017-03-17 15:49:43 (GMT)
commit6bad8672803d0bb4f45cba795b132eb1dc0061a8 (patch)
treefc9f18477b9eda648e24709f13f4a104ae9ff7cb
parent45d83369a3a46783fa9d2106952ab6ef9d7d5d9d (diff)
Issue #2853823 by Jo Fitzgerald, claudiu.cristea, Munavijayalakshmi, phenaproxima, alexpott, heddn: Explode processor is too strict regarding to the input value
-rw-r--r--core/modules/migrate/src/Plugin/migrate/process/Explode.php68
-rw-r--r--core/modules/migrate/tests/src/Unit/process/ExplodeTest.php59
2 files changed, 105 insertions, 22 deletions
diff --git a/core/modules/migrate/src/Plugin/migrate/process/Explode.php b/core/modules/migrate/src/Plugin/migrate/process/Explode.php
index ef7c94a..536a9e1 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/Explode.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/Explode.php
@@ -22,6 +22,11 @@ use Drupal\migrate\Row;
* 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 @@ use Drupal\migrate\Row;
* 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 @@ use Drupal\migrate\Row;
* 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 @@ use Drupal\migrate\Row;
* $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);
}
/**
diff --git a/core/modules/migrate/tests/src/Unit/process/ExplodeTest.php b/core/modules/migrate/tests/src/Unit/process/ExplodeTest.php
index 3245e60..20bf3a0 100644
--- a/core/modules/migrate/tests/src/Unit/process/ExplodeTest.php
+++ b/core/modules/migrate/tests/src/Unit/process/ExplodeTest.php
@@ -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 @@ class ExplodeTest extends MigrateProcessTestCase {
/**
* 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');
}