Plum is a data processing pipeline that helps you to write structured, reusable and well tested data processing code.
Index Workflow Readers Writers Filters Converters Extensions
Converters take an item and convert it into something else. A converter can be either a class that implements
Plum\Plum\Converter\ConverterInterface or a function.
A converter is attached to the workflow by calling the addConverter() method. The addConverter() method expects an
array with options as its parameter.
In the following example we are going to attach Plum\Plum\Converter\NullConverter, which replaces null values with
the empty string.
use Plum\Plum\Converter\NullConverter;
$workflow = new Workflow();
$workflow->addConverter(['converter' => new NullConverter()]);Since the above code is pretty common you can pass the object directly to addConverter():
use Plum\Plum\Converter\NullConverter;
$workflow = new Workflow();
$workflow->addConverter(new NullConverter());We can also use a function as converter:
$workflow = new Workflow();
$workflow->addConverter(['converter' => function ($item) {
$item['name'] = mb_strtoupper($item['name'];
return $item;
}]);There is also a shortcut for passing in a function:
$workflow = new Workflow();
$workflow->addConverter(function ($item) {
$item['name'] = mb_strtoupper($item['name'];
return $item;
});By default converters are always prepended at the end of the workflow. However, you can change the default behaviour
by using the position option.
$workflow = new Workflow();
$workflow->addConverter([
'converter' => new NullConverter(),
'position' => Workflow::PREPEND,
]);Often you don't want to convert the whole item, but only a single field and Plum therefore supports value converters.
Plum uses Vale to retrieve a field from an arbitrary array or object, apply a converter
and stores the converted value back into its original position. To use a value converter you need to pass the access
key to the value as the field option.
$writer = new ArrayWriter();
$workflow = new Workflow();
$workflow->addConverter([
'field' => 'foo',
'converter' => function ($v) { return $v*2; }
);
$workflow->addWriter($writer);
$workflow->process(new ArrayReader(['foo' => 21, 'bar' => 11, 'qoo' => 69, 'qoz' => 100]));
$writer->getData(); // -> ['foo' => 42, 'bar' => 11, 'qoo' => 69, 'qoz' => 100]Value converters also support filters and you can prepend them to the workflow.
By default converters are applied to every item passed through the workflow. However, sometimes you only want to convert items that match a certain criteria. You can use conditional converters for htis.
The addConverter() method accepts an filter, that is, an instance Plum\Plum\Filter\FilterInterface. If a
filter is provided the converter is only applied to an item if the filter returns true for the given item. Otherwise
the original item is returned by the converter.
$workflow->addConverter([
'converter' => function ($item) { return mb_strtoupper($item); },
'filter' => function ($item) { return preg_match('/foo/', $item); }
]);
// "foobar" -> "FOOBAR"
// "bazbar" -> "bazbar"Conditional converters also work for value converters.
$workflow->addConverter([
'field' => 'k',
'converter' => function ($item) { return mb_strtoupper($item); },
'filter' => function ($item) { return !empty($item['a']); }
]);
// ["k" => "foobar", "a" => "xyz"] -> ["k" => "FOOBAR", "a" => "xyz"]
// ["k" => "foobar", "a" => ""] -> ["k" => "foobar", "a" => ""]
// ["k" => "bazbar", "a" => "xyz"] -> ["k" => "bazbar", "a" => "xyz"]As you can see in the previous example, the field option is only applied to the converter, not the filter. If you
want to filter on a specific field you can use the filterField option.
$workflow->addConverter([
'field' => 'k',
'converter' => function ($item) { return mb_strtoupper($item); },
'filterField' => 'k',
'filter' => function ($item) { return preg_match('/foo/', $item); }
]);
// ["k" => "foobar"] -> ["k" => "FOOBAR"]
// ["k" => "bazbar"] -> ["k" => "bazbar"]The core Plum library contains some basic and general converters.
The Plum\Plum\Converter\CallbackConverter calls a callback to convert a given item.
use Plum\Plum\Converter\CallbackConverter;
$converter = new CallbackConverter(function ($item) { return mb_strtoupper($item); });
$converter->convert('foo'); // -> FOOAs you can see that this has the same effect as passing a function directly to addConverter() and in fact, if you
pass a function to addConverter() Plum internally wraps the function in a CallbackConverter.
You can use Plum\Plum\Converter\HeaderConverter to make the first item in the workflow the header and use these values
as keys for the other items. This is the case, for example, in CSV or Excel files where the first row contains the
names of the columns.
use Plum\Plum\Converter\HeaderConverter;
$workflow->addConverter(new HeaderConverter());
$workflow->addFilter(new SkipFirstFilter(1)); // Remove header
$workflow->process(new ArrayReader([['name', 'age'], ['x', 21], ['y', 42]]));
// -> [["name" => "x", "age" => 21], ["name" => "y", "age" => 42]]Plum\Plum\Converter\LogConverter logs the item to a PSR-3 compatible logger. The
item is not modified by this converter. In addition to the logger you can also pass the desired log level and the log
message to the constructor.
use Plum\Plum\Converter\LogConverter;
$converter = new LogConverter($logger, 'debug', 'Item:');
$converter->convert(['foo' => 'bar']);
// Will call
// $logger->log('debug', 'Item:', ['foo' = 'bar']);The MappingConverter can be used to maps a value from one element to another. The converter uses
Vale to target elements in complex, nested data structures (objects and arrays); the
given item therefore can be even a complex object. Vale tries a lot of different variants to access an element, thus
it should be possible to transform nearly every item.
use Plum\Plum\Converter\MappingConverter;
$converter = new MappingConverter();
$converter->addMapping(['foo'], ['bar']);
$converter->convert(['foo' => 'foobar']); // -> ['bar' => 'foobar']If two or more mappings which point to the same target element are given, the last one added to the converter takes precedence and the first element will be lost.
$converter = new MappingConverter();
$converter->addMapping(['foo'], ['bar']);
$converter->addMapping(['qoo'], ['bar']);
$converter->convert(['foo' => 'foobar', 'qoo' => 'qoobar']); // -> ['bar' => 'qoobar']If the from mapping is empty, the converter creates a new array and sets the item as to element in that array:
$converter->addMapping('', 'bar');
$converter->convert('foobar'); // -> ['bar' => 'foobar']In contrast, if the to mapping is empty the from value will returned as item.
$converter->addMapping('bar', '');
$converter->convert(['bar' => 'foobar'); // -> 'foobar'Plum\Plum\Converter\NullConverter converts null values into a null value (by default the empty string).
use Plum\Plum\Converter\NullConverter;
$converter = new NullConverter();
$converter->convert(null); // -> ""You can also define which value null values should have assigned to.
use Plum\Plum\Converter\NullConverter;
$converter = new NullConverter(0);
$converter->convert(null); // -> 0You can add custom converters by creating a class that implements Plum\Plum\Converter\ConverterInterface.
use Plum\Plum\Converter\ConverterInterface;
class Rot13Converter implements ConverterInterface
{
public function convert($item)
{
return str_rot13($item);
}
}Index Workflow Readers Writers Filters Converters Extensions