# Converters
**Converters** are callables that can be invoked to transform a value into another type.
Specials converters are :
- [Printers](Printers.md) that transform any value into a string
- [Coercers](Coercers.md) that transform any value into a php primitive type or a php class
## Get Started
A converter can be :
- a function
```php
function convert(mixed $value, array $options = []): mixed {}
```
- a class implementing <@\MMO\core\interfaces\iConverter>. An instance of this class can be invoked like a function.
```php fragment
class MyConverter implements \MMO\core\interfaces\iConverter {..}
$converter = new MyConverter();
$value = 25;
//$value === (int)25
$value = $converter($value, ['type' => 'string']);//transform value into a string
//$value === (string)"25"
```
## Implementation using abstract class <@\MMO\core\classes\aConverter>
This abstract class helps to build a generic converter.
It is a [singleton](@MMO\core\interfaces\iSingleton), meaning it has only one instance, accessible with the static method `::Shared()`.
Basically a converter associate its method to a given user defined type.
The type used to convert a value is either determined by the converter, or given as a parameter of the options (`['type' => 'xxx']`).
```php
class MyConverter extends \MMO\core\classes\aConverter {
//protected constructor, so it is not possible to do `new MyConverter()`
protected function __construct() {
$this->setFunction('string', [$this, 'convertToString']);
}
public function convertToString(mixed $value, array $opts = []): string {
return (string)$value;
}
public function __invoke(mixed $value, array $options = []) {
$type = $options['type']??$this->getType($value);
return $this->call($type, $value, $options);
}
}
```
You can define default options using the <@\MMO\core\classes\aConverter::addOptions()>, <@\MMO\core\classes\aConverter::setOptions()> methods.
These options will be automatically given to the converter's function.
```php fragment
class MyConverter ... {
...
public function convertToString(mixed $value, array $opts = []): string {
if (is_array($value)) return implode($opts['separator'], $value);
return (string)$value;
}
}
$converter = MyConverter::Shared();
//no default options
echo $converter(['A', 'B'], ['type' => 'string', 'separator' => ',']); // => "A,B"
//set default options
$converter->setOptions('string', ['separator' => ',']);
echo $converter(['A', 'B'], ['type' => 'string']); // => "A,B"
//other way to call it
echo $converter->call('string', ['A', 'B']); // => "A,B"
```
## Implementation using abstract class <@\MMO\core\classes\aExporter>
Exporter is a converter that use the value's class or primitive type to guess the type.
To guess the correct class type to use, it looks at the value's class, then the value's parent classes and interfaces classes.
If no function are registered for these classes, the type `instance` is used.
WARNING: trait cannot be detected, it is useless to register a trait as a converter type.
### Types
Basic known types are (each have their own method in the exporter class):
| type | PHP gettype | method |
|----------|--------------------------------------------------------------------------------|--------------------|
| instance | object (which classes and interfaces are not registered as a converter's type) | exportInstance(..) |
| array | array (list only. index are numerical and starts from 0) | exportArray(..) |
| object | array (not a list) | exportObject(..) |
| null | NULL | exportNull(..) |
| integer | int | exportInteger(..) |
| number | double (float) | exportNumber(..) |
| string | string | exportString(..) |
| boolean | boolean | exportBoolean(..) |
| enum | object (extends `UnitEnum`) | exportEnum(..) |
Exporter are safe for recursive objects. It uses a hash function to remember any object it has exported
(using a [WeakMap](https://www.php.net/manual/en/class.weakmap.php)) and returns the hash when it encounters the same object twice.
`exportInstance` exports an object's properties, protected and private ones included, but not its static ones (they
are its class's: its caches...) nor an uninitialized one, which has no value -- the properties `(array)$object` holds.
A printer (`TextPrinter`, `HtmlPrinter`...) also leaves out a property marked `#[NoDump]`, as a dump does
(`exportsProperty()` decides).
### Using property's attribute
To export an object, the exporter uses a php [Attribute](https://www.php.net/manual/en/language.attributes.overview.php).
The attribute's class used is stored in the exporter's public property `$exportAttribute`
```php
class MyExporter extends \MMO\core\classes\aExporter {
public function __construct() {
parent::__construct();
//override the export for class MyObject
$this->setFunction(MyObject::class, [$this, 'exportMyObject']);
}
//this function is used to prevent cyclic reference
public function hash(object $value, array $options = []): mixed {
return get_class($value);
}
//this function is called to export list array
public function exportArray(array $value, array $options = []) {
return implode($options['separator']??',', $value);
}
//this is just an example of how to override the export of a class.
// (when not declared, it automatically calls `exportInstance`)
public function exportMyObject(object $value, array $options = []) {
return $this->exportInstance($value, $options);
}
}
class MyObject {
//this is how you pass options to the export function.
#[MyExport(['separator' => '|'])]
protected $x = [1,2];
}
\MMO\dump(MyExporter::Export(new MyObject()));
// {x: "1|2"}
```
- You can change any export function dynamically by calling `setFunction()` on the exporter
```php
MyExporter::Shared()->setFunction('integer', fn($value) => (string)$value+1);
echo MyExporter::Export(1);
//"2"
```
## References
### See also
- [printers](Printers.md)
## Tags
<#coercer>, <#printer>, <#converter>, <#attribute>