Skip to content

Commit

Permalink
Improve codebase
Browse files Browse the repository at this point in the history
- BOM value are moved to an Interface
- BOM functions are introduced
- XML conversion is moved to its own class
- MapIterator is improved and simplified
  • Loading branch information
nyamsprod committed Mar 16, 2017
1 parent 473a0b9 commit c4412d9
Show file tree
Hide file tree
Showing 17 changed files with 478 additions and 179 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,9 @@ All Notable changes to `Csv` will be documented in this file
- `League\Csv\BOM`
- `League\Csv\Statement`
- `League\Csv\RecordSet`
- `League\Csv\XMLEncoder`
- `League\Csv\is_bom` function
- `League\Csv\bom_match` function
- `League\Csv\Exception\CsvException`
- `League\Csv\Exception\InsertionException`
- `League\Csv\Exception\InvalidArgumentException`
Expand Down
2 changes: 2 additions & 0 deletions autoload.php
@@ -1,5 +1,7 @@
<?php

require __DIR__.DIRECTORY_SEPARATOR.'src'.DIRECTORY_SEPARATOR.'functions_include.php';

spl_autoload_register(function ($class) {

$prefix = 'League\Csv\\';
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -29,7 +29,8 @@
"autoload": {
"psr-4": {
"League\\Csv\\": "src"
}
},
"files": ["src/functions_include.php"]
},
"autoload-dev": {
"psr-4": {
Expand Down
32 changes: 1 addition & 31 deletions docs/9.0/connections/bom.md
Expand Up @@ -5,7 +5,6 @@ title: BOM sequence detection and addition

# BOM sequences


## Detecting the BOM sequence

~~~php
Expand All @@ -16,12 +15,9 @@ const BOM::UTF16_BE = "\xFE\xFF";
const BOM::UTF16_LE = "\xFF\xFE";
const BOM::UTF32_BE = "\x00\x00\xFE\xFF";
const BOM::UTF32_LE = "\xFF\xFE\x00\x00";

public static BOM::match(string $str): string
public static BOM::isValid(string $sequence): bool
~~~

To improve interoperability with programs interacting with CSV, the package now provide a Enum like class `BOM` to help you detect the presence of the <abbr title="Byte Order Mark">BOM</abbr> sequence in any string.
To improve interoperability with programs interacting with CSV, the package now provide an interface `BOM` to help you detect set the appropriate <abbr title="Byte Order Mark">BOM</abbr> sequence.

### Constants

Expand All @@ -33,32 +29,6 @@ To ease BOM sequence manipulation, the `BOM` class provides the following consta
* `BOM::UTF32_BE` : `UTF-32` `BOM` with Big-Endian;
* `BOM::UTF32_LE` : `UTF-32` `BOM` with Little-Endian;

### Methods

~~~php
<?php

public static BOM::match(string $str): string
public static BOM::isValid(string $sequence): bool
~~~

If you have a string and wonders if it begins with a known BOM sequence you can use the `BOM::match` method. This method will try to detect the correct BOM sequence at the start of your string. If no BOM sequence is found, an empty string is returned.

At any given time you can validate your BOM sequence against the class BOM list.

~~~php
<?php

use League\Csv\BOM;

$str = BOM::UTF8.'The quick brown fox jumps over the lazy dog';
$found = BOM::match($str); //returns "\xEF\xBB\xBF"
BOM::isValid($found); //return true
str2 = 'The quick brown fox jumps over the lazy dog';
$found2 = BOM::match($str2); //returns ""
BOM::isValid($found2); //return false
~~~

## Managing CSV documents BOM sequence

### Detecting the BOM sequence
Expand Down
2 changes: 1 addition & 1 deletion docs/upgrading/upgrading-9.0.md
Expand Up @@ -333,7 +333,7 @@ $records = $stmt->process($reader)->fetchAll();

### BOM handling

The BOM sequence constants are now attached to a `BOM` enum like class
The BOM sequence constants are now attached to a `BOM` interface

Before:

Expand Down
5 changes: 4 additions & 1 deletion phpunit.xml
Expand Up @@ -18,8 +18,11 @@
</testsuites>

<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<whitelist>
<directory suffix=".php">src</directory>
<exclude>
<directory suffix="include.php">src</directory>
</exclude>
</whitelist>
</filter>

Expand Down
3 changes: 2 additions & 1 deletion src/AbstractCsv.php
Expand Up @@ -17,6 +17,7 @@
use League\Csv\Exception\LogicException;
use League\Csv\Exception\RuntimeException;
use SplFileObject;
use function League\Csv\bom_match;

/**
* An abstract class to enable basic CSV manipulation
Expand Down Expand Up @@ -218,7 +219,7 @@ public function getInputBOM(): string
if (null === $this->input_bom) {
$this->document->setFlags(SplFileObject::READ_CSV);
$this->document->rewind();
$this->input_bom = BOM::match($this->document->fgets());
$this->input_bom = bom_match($this->document->fgets());
}

return $this->input_bom;
Expand Down
50 changes: 1 addition & 49 deletions src/BOM.php
Expand Up @@ -14,8 +14,6 @@

namespace League\Csv;

use ReflectionClass;

/**
* Defines constants for common BOM sequences
*
Expand All @@ -24,7 +22,7 @@
* @author Ignace Nyamagana Butera <nyamsprod@gmail.com>
*
*/
final class BOM
interface BOM
{
/**
* UTF-8 BOM sequence
Expand All @@ -50,50 +48,4 @@ final class BOM
* UTF-32 LE BOM sequence
*/
const UTF32_LE = "\xFF\xFE\x00\x00";

/**
* Returns all possible BOM sequences as an array
*
* @return string[]
*/
private static function toArray(): array
{
static $cache;

$cache = $cache ?? (new ReflectionClass(BOM::class))->getConstants();

return $cache;
}

/**
* Returns the BOM sequence found at the start of the string
*
* If no valid BOM sequence is found an empty string is returned
*
* @param string $str
*
* @return string
*/
public static function match(string $str): string
{
foreach (self::toArray() as $sequence) {
if (0 === strpos($str, $sequence)) {
return $sequence;
}
}

return '';
}

/**
* Tell whether the submitted sequence is a valid BOM sequence
*
* @param string $sequence
*
* @return bool
*/
public static function isValid(string $sequence): bool
{
return in_array($sequence, self::toArray(), true);
}
}
14 changes: 6 additions & 8 deletions src/MapIterator.php
Expand Up @@ -14,8 +14,8 @@

namespace League\Csv;

use Iterator;
use IteratorIterator;
use Traversable;

/**
* A simple MapIterator
Expand All @@ -33,15 +33,15 @@ class MapIterator extends IteratorIterator
*
* @var callable
*/
private $callable;
protected $callable;

/**
* The Constructor
*
* @param Iterator $iterator
* @param callable $callable
* @param Traversable $iterator
* @param callable $callable
*/
public function __construct(Iterator $iterator, callable $callable)
public function __construct(Traversable $iterator, callable $callable)
{
parent::__construct($iterator);
$this->callable = $callable;
Expand All @@ -52,8 +52,6 @@ public function __construct(Iterator $iterator, callable $callable)
*/
public function current()
{
$iterator = $this->getInnerIterator();

return ($this->callable)($iterator->current(), $iterator->key());
return ($this->callable)(parent::current(), $this->key());
}
}
87 changes: 20 additions & 67 deletions src/RecordSet.php
Expand Up @@ -17,7 +17,6 @@
use CallbackFilterIterator;
use Countable;
use DOMDocument;
use DOMElement;
use Generator;
use Iterator;
use IteratorAggregate;
Expand Down Expand Up @@ -178,20 +177,18 @@ protected function convertToUtf8(Iterator $iterator): Iterator
return $iterator;
}

$convert_cell = function ($value) : string {
return mb_convert_encoding((string) $value, 'UTF-8', $this->conversion_input_encoding);
$walker = function (&$value, &$offset) {
$value = mb_convert_encoding((string) $value, 'UTF-8', $this->conversion_input_encoding);
$offset = mb_convert_encoding((string) $offset, 'UTF-8', $this->conversion_input_encoding);
};

$convert_record = function (array $record) use ($convert_cell): array {
$res = [];
foreach ($record as $key => $value) {
$res[$convert_cell($key)] = $convert_cell($value);
}
$convert = function (array $record) use ($walker): array {
array_walk($record, $walker);

return $res;
return $record;
};

return new MapIterator($iterator, $convert_record);
return new MapIterator($iterator, $convert);
}

/**
Expand All @@ -218,7 +215,7 @@ public function toHTML(
* Transforms a CSV into a XML
*
* @param string $root_name XML root node name
* @param string $row_name XML row node name
* @param string $record_name XML row node name
* @param string $cell_name XML cell node name
* @param string $column_attr XML column attribute name
* @param string $offset_attr XML offset attribute name
Expand All @@ -227,66 +224,22 @@ public function toHTML(
*/
public function toXML(
string $root_name = 'csv',
string $row_name = 'row',
string $record_name = 'row',
string $cell_name = 'cell',
string $column_attr = 'name',
string $offset_attr = 'offset'
): DOMDocument {
$doc = new DOMDocument('1.0', 'UTF-8');
$root = $doc->createElement($root_name);
foreach ($this->convertToUtf8($this->iterator) as $offset => $row) {
$root->appendChild($this->toDOMNode(
$doc,
$row,
$offset,
$row_name,
$cell_name,
$column_attr,
$offset_attr
));
}
$doc->appendChild($root);

return $doc;
}

/**
* convert a Record into a DOMNode
*
* @param DOMDocument $doc The DOMDocument
* @param array $row The CSV record
* @param int $offset The CSV record offset
* @param string $row_name XML row node name
* @param string $cell_name XML cell node name
* @param string $column_attr XML header attribute name
* @param string $offset_attr XML offset attribute name
*
* @return DOMElement
*/
protected function toDOMNode(
DOMDocument $doc,
array $row,
int $offset,
string $row_name,
string $cell_name,
string $column_attr,
string $offset_attr
): DOMElement {
$rowElement = $doc->createElement($row_name);
if ($this->preserve_offset) {
$rowElement->setAttribute($offset_attr, (string) $offset);
}
foreach ($row as $name => $value) {
$content = $doc->createTextNode($value);
$cell = $doc->createElement($cell_name);
if (!empty($this->column_names)) {
$cell->setAttribute($column_attr, $name);
}
$cell->appendChild($content);
$rowElement->appendChild($cell);
}

return $rowElement;
$encoder = (new XMLEncoder())
->rootName($root_name)
->recordName($record_name)
->itemName($cell_name)
->columnAttributeName($column_attr)
->offsetAttributeName($offset_attr)
->preserveItemOffset(!empty($this->column_names))
->preserveRecordOffset($this->preserve_offset)
;

return $encoder->encode($this->convertToUtf8($this->iterator));
}

/**
Expand Down
27 changes: 14 additions & 13 deletions src/Statement.php
Expand Up @@ -189,20 +189,21 @@ public function process(Reader $reader): RecordSet
*/
protected function buildColumns(array $columns): array
{
$combine = null;
if (!empty($this->columns)) {
$columns_alias = $this->filterColumnAgainstCsvHeader($columns);
$columns = array_values($columns_alias);
$combine = function (array $record) use ($columns_alias): array {
$res = [];
foreach ($columns_alias as $key => $alias) {
$res[$alias] = $record[$key] ?? null;
}

return $res;
};
if (empty($this->columns)) {
return [$columns, null];
}

$columns_alias = $this->filterColumnAgainstCsvHeader($columns);
$columns = array_values($columns_alias);
$combine = function (array $record) use ($columns_alias): array {
$res = [];
foreach ($columns_alias as $key => $alias) {
$res[$alias] = $record[$key] ?? null;
}

return $res;
};

return [$columns, $combine];
}

Expand All @@ -224,7 +225,7 @@ protected function filterColumnAgainstCsvHeader(array $headers)
return $this->columns;
}

throw new RuntimeException('If no header is specified the columns keys must contain only integer');
throw new RuntimeException('If no header is specified the columns keys must contain only positive integer or 0');
}

$columns = $this->formatColumns($this->columns);
Expand Down

0 comments on commit c4412d9

Please sign in to comment.