Compare commits

..

5 commits
master ... 1.0

Author SHA1 Message Date
Simon Vieille 8ff4db14a2 packaging 2016-05-23 20:59:27 +02:00
Simon Vieille a09bb929a0 packaging 2016-05-23 20:43:08 +02:00
Simon Vieille 0c24bf2e3a packaging 2016-05-23 20:32:12 +02:00
Simon Vieille ea8c66512a packaging 2016-05-23 20:31:44 +02:00
Simon Vieille a87103518a packaging 2016-05-23 20:31:04 +02:00
14 changed files with 139 additions and 309 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
*.swp
tags
vendor

View file

@ -1,14 +0,0 @@
build_settings:
verbose: false
prefer_symlink: false
setup:
composer:
action: "install"
test:
php_unit:
directory: "tests/"
args: "--configuration 'phpunit.xml'"
complete:

25
Jenkinsfile vendored
View file

@ -1,25 +0,0 @@
pipeline {
agent any
stages {
stage('PHP 7.1') {
steps {
sh '/usr/local/bin/composer-php7.1 install'
sh 'php7.1 /usr/local/bin/phpunit-7'
}
}
stage('PHP 7.3') {
steps {
sh '/usr/local/bin/composer-php7.3 update'
sh 'php7.3 /usr/local/bin/phpunit-9'
}
}
stage('PHP 7.4') {
steps {
sh '/usr/local/bin/composer-php7.4 update'
sh 'php7.4 /usr/local/bin/phpunit-9'
}
}
}
}

View file

@ -20,5 +20,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,4 +0,0 @@
.PHONY: tests
tests:
phpunit

View file

@ -1,22 +1,16 @@
csv-validator
=============
[![Build Status](https://ci.gitnet.fr/buildStatus/icon?job=Gitnet%2Fcsv-validator%2F3)](https://ci.gitnet.fr/job/Gitnet/job/csv-validator/job/3/)
CSV validator library
That uses the constraints of Symfony framework: [http://symfony.com/doc/current/reference/constraints.html](http://symfony.com/doc/current/reference/constraints.html).
* [Installation](#installation)
* [Example](#example)
* [Contributors](#contributors)
Installation
------------
You need [composer](https://getcomposer.org/):
composer require deblan/csv-validator
composer require deblan/csv-validator dev-master
Example
@ -35,8 +29,12 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
require __DIR__.'/vendor/autoload.php';
// Initialisation of the parser
$parser = new CsvParser(__DIR__.'/tests/fixtures/example.csv');
$parser->setHasLegend(true);
// Initialisation of the validator
$validator = new Validator();
$validator = new Validator($parser, Validation::createValidator());
// The first field must contain an email
$validator->addFieldConstraint(0, new Email());
@ -45,7 +43,7 @@ $validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Date());
// Validate the legend
$validator->setExpectedHeaders(['foo', 'bar', 'bim']);
$validator->setExceptedLegend(array('foo', 'bar', 'bim'));
// An line must contain 3 columns
$validator->addDataConstraint(new Callback(function($data, ExecutionContextInterface $context) {
@ -54,18 +52,14 @@ $validator->addDataConstraint(new Callback(function($data, ExecutionContextInter
}
}));
// Initialisation of the parser
$parser = new CsvParser();
$parser->setHasHeaders(true);
$validator->validate($parser->parseFile(__DIR__.'/tests/fixtures/example.csv'));
$validator->validate();
if ($validator->isValid() === false) {
foreach ($validator->getErrors() as $error) {
$line = $error->getLine();
$line = $error->getLine();
$column = $error->getColumn();
$message = $error->getViolation()->getMessage();
echo <<<EOF
<ul>
<li>Line: $line</li>
@ -80,41 +74,4 @@ EOF;
}
```
Run `example.php` and see results:
```
<ul>
<li>Line: 1</li>
<li>Column: </li>
<li>
<p>Invalid legend.</p>
</li>
</ul>
<ul>
<li>Line: 4</li>
<li>Column: </li>
<li>
<p>The line must contain 3 columns</p>
</li>
</ul>
<ul>
<li>Line: 2</li>
<li>Column: 1</li>
<li>
<p>This value is not a valid email address.</p>
</li>
</ul>
<ul>
<li>Line: 3</li>
<li>Column: 2</li>
<li>
<p>This value is not a valid date.</p>
</li>
</ul>
```
Contributors
------------
* Simon Vieille
* Gautier Deruette

View file

@ -15,8 +15,8 @@
},
"minimum-stability": "dev",
"require": {
"php": "^7.1.3",
"symfony/validator": "^4",
"deblan/csv": "v2.0.*|v3.*"
"php": ">=5.6.0",
"symfony/validator": "2.*",
"deblan/csv": "dev-master"
}
}

View file

@ -2,6 +2,7 @@
use Deblan\Csv\CsvParser;
use Deblan\CsvValidator\Validator;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Date;
use Symfony\Component\Validator\Constraints\Callback;
@ -9,8 +10,12 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
require __DIR__.'/vendor/autoload.php';
// Initialisation of the parser
$parser = new CsvParser(__DIR__.'/tests/fixtures/example.csv');
$parser->setHasLegend(true);
// Initialisation of the validator
$validator = new Validator();
$validator = new Validator($parser, Validation::createValidator());
// The first field must contain an email
$validator->addFieldConstraint(0, new Email());
@ -19,27 +24,23 @@ $validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Date());
// Validate the legend
$validator->setExpectedHeaders(['foo', 'bar', 'bim']);
$validator->setExceptedLegend(array('foo', 'bar', 'bim'));
// An line must contain 3 columns
$validator->addDataConstraint(new Callback(function ($data, ExecutionContextInterface $context) {
$validator->addDataConstraint(new Callback(function($data, ExecutionContextInterface $context) {
if (count($data) !== 6) { // 6 because of the legend (3 fields * 2)
$context->addViolation('The line must contain 3 columns');
}
}));
// Initialisation of the parser
$parser = new CsvParser();
$parser->setHasHeaders(true);
$validator->validate($parser->parseFile(__DIR__.'/tests/fixtures/example.csv'));
$validator->validate();
if ($validator->isValid() === false) {
foreach ($validator->getErrors() as $error) {
$line = $error->getLine();
$line = $error->getLine();
$column = $error->getColumn();
$message = $error->getViolation()->getMessage();
echo <<<EOF
<ul>
<li>Line: $line</li>

View file

@ -7,6 +7,7 @@
convertWarningsToExceptions = "true"
processIsolation = "false"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "vendor/autoload.php" >
<testsuites>

View file

@ -7,11 +7,9 @@ use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Validator\RecursiveValidator;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\Validation;
/**
* Class Validator.
*
* Class Validator
* @author Simon Vieille <simon@deblan.fr>
*/
class Validator
@ -37,7 +35,7 @@ class Validator
protected $dataConstraints = [];
/**
* @var bool
* @var boolean
*/
protected $hasValidate = false;
@ -45,32 +43,30 @@ class Validator
* @var array
*/
protected $errors = [];
/**
* @var array
*/
protected $expectedHeaders = [];
protected $expectedLegend = [];
/**
* Constructor.
* Constructor
*
* @param CsvParser $parser
* @param RecursiveValidator $validator
*/
public function __construct(RecursiveValidator $validator = null)
public function __construct(CsvParser $parser, RecursiveValidator $validator)
{
if ($validator === null) {
$validator = Validation::createValidator();
}
$this->parser = $parser;
$this->parser->parse();
$this->validator = $validator;
}
/**
* Append a constraint to a specific column.
* Append a constraint to a specific column
*
* @param int $key The column number
* @param $key The column number
* @param Constraint $constraint The constraint
*
* @return Validator
*/
public function addFieldConstraint($key, Constraint $constraint)
@ -85,10 +81,10 @@ class Validator
}
/**
* Append a constraint to a specific line.
* Append a constraint to a specific line
*
* @param $key The column number
* @param Constraint $constraint The constraint
*
* @return Validator
*/
public function addDataConstraint(Constraint $constraint)
@ -99,63 +95,54 @@ class Validator
}
/**
* Set the expected legend.
* Set the excepted legend
*
* @param array $legend Expected legend
*
* @return Validator
*/
public function setExpectedHeaders(array $legend)
public function setExceptedLegend(array $legend)
{
$this->expectedHeaders = $legend;
$this->expectedLegend = $legend;
return $this;
}
/**
* Run the validation.
*
* @param CsvParser $parser
* Run the validation
*/
public function validate(CsvParser $parser)
public function validate()
{
if ($this->parser !== $parser) {
$this->parser = $parser;
$this->errors = [];
$this->hasValidate = false;
} elseif ($this->hasValidate) {
if ($this->hasValidate) {
return;
}
$this->validateHeaders();
$this->validateLegend();
$this->validateDatas();
$this->validateFields();
$this->hasValidate = true;
}
/**
* Validates the legend.
*/
protected function validateHeaders()
protected function validateLegend()
{
if (!$this->parser->getHasHeaders()) {
if (!$this->parser->getHasLegend()) {
return;
}
if (empty($this->expectedHeaders)) {
if (empty($this->expectedLegend)) {
return;
}
if ($this->parser->getHeaders() !== $this->expectedHeaders) {
if ($this->parser->getLegend() !== $this->expectedLegend) {
$this->mergeErrorMessage('Invalid legend.', 1);
}
}
/**
* Validates datas.
* Validates datas
*/
protected function validateDatas()
protected function validateDatas()
{
if (empty($this->dataConstraints)) {
return;
@ -163,7 +150,7 @@ class Validator
foreach ($this->parser->getDatas() as $line => $data) {
foreach ($this->dataConstraints as $constraint) {
$violations = $this->validator->validate($data, $constraint);
$violations = $this->validator->validateValue($data, $constraint);
$this->mergeViolationsMessages($violations, $this->getTrueLine($line));
}
@ -171,30 +158,30 @@ class Validator
}
/**
* Validates fields.
* Validates fields
*/
protected function validateFields()
protected function validateFields()
{
if (empty($this->fieldConstraints)) {
return;
}
foreach ($this->parser->getDatas() as $line => $data) {
foreach ($this->fieldConstraints as $key => $constraints) {
if (!isset($data[$key])) {
$column = $this->getTrueColunm($key);
$this->mergeErrorMessage(
sprintf('Field "%s" does not exist.', $column),
$this->getTrueLine($line),
sprintf('Field "%s" does not exist.', $column),
$this->getTrueLine($line),
$column
);
} else {
foreach ($constraints as $constraint) {
$violations = $this->validator->validate($data[$key], $constraint);
$violations = $this->validator->validateValue($data[$key], $constraint);
$this->mergeViolationsMessages(
$violations,
$this->getTrueLine($line),
$violations,
$this->getTrueLine($line),
$this->getTrueColunm($key)
);
}
@ -204,11 +191,11 @@ class Validator
}
/**
* Add violations.
* Add violations
*
* @param ConstraintViolationList $violations
* @param int $line The line of the violations
* @param int|null $key The column of the violations
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
*/
protected function mergeViolationsMessages(ConstraintViolationList $violations, $line, $key = null)
{
@ -222,11 +209,11 @@ class Validator
}
/**
* Create and append a violation from a string error.
* Create and append a violation from a string error
*
* @param string $message The error message
* @param int $line The line of the violations
* @param int|null $key The column of the violations
* @param string $message The error message
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
*/
protected function mergeErrorMessage($message, $line, $key = null)
{
@ -235,9 +222,9 @@ class Validator
}
/**
* Returns the validation status.
* Returns the validation status
*
* @return bool
* @return boolean
* @throw RuntimeException No validation yet
*/
public function isValid()
@ -250,7 +237,7 @@ class Validator
}
/**
* Returns the errors.
* Returns the errors
*
* @return array
*/
@ -260,55 +247,51 @@ class Validator
}
/**
* Generate a ConstraintViolation.
* Generate a ConstraintViolation
*
* @param string $message The error message
*
* @return ConstraintViolation
*/
protected function generateConstraintViolation($message)
protected function generateConstraintViolation($message)
{
return new ConstraintViolation($message, $message, [], null, '', null);
}
/**
* Generate a Violation.
*
* @param string $message The error message
* @param int $line The line of the violations
* @param int|null $key The column of the violations
* Generate a Violation
*
* @param string $message The error message
* @param integer $line The line of the violations
* @param integer|null $key The column of the violations
* @return Violation
*/
protected function generateViolation($line, $key, ConstraintViolation $violation)
protected function generateViolation($line, $key, ConstraintViolation $violation)
{
return new Violation($line, $key, $violation);
}
/**
* Get the true line number of an error.
* Get the true line number of an error
*
* @param int $line
*
* @return int
* @param integer $line
* @return integer
*/
protected function getTrueLine($line)
protected function getTrueLine($line)
{
if ($this->parser->getHasHeaders()) {
++$line;
if ($this->parser->getHasLegend()) {
$line++;
}
return ++$line;
}
/**
* Get the true culumn number of an error.
* Get the true culumn number of an error
*
* @param int $key
*
* @return int
* @param integer $key
* @return integer
*/
protected function getTrueColunm($key)
protected function getTrueColunm($key)
{
return ++$key;
}

View file

@ -5,32 +5,31 @@ namespace Deblan\CsvValidator;
use Symfony\Component\Validator\ConstraintViolation;
/**
* Class Violation.
*
* Class Violation
* @author Simon Vieille <simon@deblan.fr>
*/
class Violation
{
/**
* @var int
* @var integer
*/
protected $line;
/**
* @var int
* @var integer
*/
protected $column;
/**
* @var ConstraintViolation
*/
protected $violation;
/**
* Constructor.
* Constructor
*
* @param int $line The line of the violation
* @param int $column The column of the violation
* @param integer $line The line of the violation
* @param integer $column The column of the violation
* @param ConstraintViolation $violation The violation
*/
public function __construct($line, $column, ConstraintViolation $violation)
@ -42,7 +41,6 @@ class Violation
/**
* @param int $line
*
* @return Violation
*/
public function setLine($line)
@ -62,7 +60,6 @@ class Violation
/**
* @param int $column
*
* @return Violation
*/
public function setColumn($column)
@ -86,7 +83,6 @@ class Violation
/**
* @param ConstraintViolation $violation
*
* @return Violation
*/
public function setViolation(ConstraintViolation $violation)

View file

@ -1,46 +0,0 @@
<?php
use PHPUnit\Framework\TestCase;
/**
* class ExampleTest.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class ExampleTest extends TestCase
{
public function testExemple()
{
$content = shell_exec('php -f '.__DIR__.'/../example.php');
$this->assertEquals('<ul>
<li>Line: 1</li>
<li>Column: </li>
<li>
<p>Invalid legend.</p>
</li>
</ul>
<ul>
<li>Line: 4</li>
<li>Column: </li>
<li>
<p>The line must contain 3 columns</p>
</li>
</ul>
<ul>
<li>Line: 2</li>
<li>Column: 1</li>
<li>
<p>This value is not a valid email address.</p>
</li>
</ul>
<ul>
<li>Line: 3</li>
<li>Column: 2</li>
<li>
<p>This value is not a valid date.</p>
</li>
</ul>
', $content);
}
}

View file

@ -1,105 +1,86 @@
<?php
use PHPUnit\Framework\TestCase;
use Deblan\Csv\CsvParser;
use Deblan\CsvValidator\Validator;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
class ValidatorTest extends TestCase
class ValidatorTest extends \PHPUnit_Framework_TestCase
{
public function testViolation()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$this->expectException('\RuntimeException');
$this->setExpectedException('\RuntimeException');
$validator->isValid();
}
public function testExpectedHeaders()
{
$parser = $this->generateParser('example.csv');
$parser->setHasHeaders(true);
$validator = $this->generateValidator();
$validator->setExpectedHeaders(['foo', 'bar', 'boo']);
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$validator = $this->generateValidator();
$validator->setExpectedHeaders(['bad', 'legend']);
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(1, count($validator->getErrors()));
}
public function testNoConstraint()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator();
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$validator = $this->generateValidator($parser);
$validator->validate();
$this->assertEquals(true, $validator->isValid());
}
public function testFieldContraintsOk()
{
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator();
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(0, new NotBlank());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$validator->validate();
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator();
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(1, new NotBlank());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$validator->validate();
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator();
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(0, new NotBlank());
$validator->addFieldConstraint(1, new NotBlank());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$validator->validate();
$this->assertEquals(true, $validator->isValid());
$this->assertEquals(0, count($validator->getErrors()));
}
public function testFieldContraintsKo()
{
$parser = $this->generateParser();
$validator = $this->generateValidator();
$validator->addFieldConstraint(0, new Email());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(2, count($validator->getErrors()));
$parser = $this->generateParser();
$validator = $this->generateValidator();
$validator->addFieldConstraint(1, new Email());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(5, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator();
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(0, new Email());
$validator->validate();
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(4, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(1, new Email());
$validator->validate();
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(4, count($validator->getErrors()));
$parser = $this->generateParser('example.csv');
$validator = $this->generateValidator($parser);
$validator->addFieldConstraint(0, new Email());
$validator->addFieldConstraint(1, new Email());
$validator->validate($parser->parseFile(__DIR__.'/fixtures/example.csv'));
$validator->validate();
$this->assertEquals(false, $validator->isValid());
$this->assertEquals(7, count($validator->getErrors()));
$this->assertEquals(8, count($validator->getErrors()));
}
protected function generateParser()
protected function generateParser($file)
{
return new CsvParser();
return new CsvParser(__DIR__.'/fixtures/'.$file);
}
protected function generateValidator()
protected function generateValidator(CsvParser $parser)
{
return new Validator(Validation::createValidator());
return new Validator($parser, Validation::createValidator());
}
}

View file

@ -1,10 +1,9 @@
<?php
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\ConstraintViolation;
use Deblan\CsvValidator\Violation;
class ViolationTest extends TestCase
class ViolationTest extends \PHPUnit_Framework_TestCase
{
public function testViolation()
{