├── src
└── Goodby
│ └── CSV
│ ├── Import
│ ├── Tests
│ │ ├── Standard
│ │ │ ├── Join
│ │ │ │ ├── csv_files
│ │ │ │ │ ├── mac-excel.csv
│ │ │ │ │ ├── utf-8.csv
│ │ │ │ │ ├── colon-separated.csv
│ │ │ │ │ ├── tab-separated.csv
│ │ │ │ │ ├── sjis.csv
│ │ │ │ │ └── issue-5.csv
│ │ │ │ ├── CSVFiles.php
│ │ │ │ ├── Observer
│ │ │ │ │ └── PdoObserverTest.php
│ │ │ │ └── LexerTest.php
│ │ │ ├── SandboxDirectoryManager.php
│ │ │ └── Unit
│ │ │ │ ├── Observer
│ │ │ │ └── SqlObserverTest.php
│ │ │ │ ├── LexerConfigTest.php
│ │ │ │ ├── StreamFilter
│ │ │ │ └── ConvertMbstringEncodingTest.php
│ │ │ │ └── InterpreterTest.php
│ │ └── Protocol
│ │ │ ├── LexerTest.php
│ │ │ └── InterpreterTest.php
│ ├── Standard
│ │ ├── Exception
│ │ │ └── StrictViolationException.php
│ │ ├── Observer
│ │ │ ├── SqlObserver.php
│ │ │ └── PdoObserver.php
│ │ ├── Lexer.php
│ │ ├── Interpreter.php
│ │ ├── StreamFilter
│ │ │ └── ConvertMbstringEncoding.php
│ │ └── LexerConfig.php
│ └── Protocol
│ │ ├── Exception
│ │ ├── InvalidLexicalException.php
│ │ └── CsvFileNotFoundException.php
│ │ ├── InterpreterInterface.php
│ │ └── LexerInterface.php
│ ├── Export
│ ├── Tests
│ │ ├── Standard
│ │ │ ├── Join
│ │ │ │ ├── csv_files
│ │ │ │ │ ├── utf-8.csv
│ │ │ │ │ ├── euc-jp.csv
│ │ │ │ │ └── multiple-lines.csv
│ │ │ │ ├── Collection
│ │ │ │ │ └── PdoCollectionTest.php
│ │ │ │ ├── UsageTest.php
│ │ │ │ └── ExporterTest.php
│ │ │ └── Unit
│ │ │ │ ├── Collection
│ │ │ │ ├── SampleAggIterator.php
│ │ │ │ └── CallbackCollectionTest.php
│ │ │ │ └── ExporterConfigTest.php
│ │ └── Protocol
│ │ │ └── ExporterInterfaceTest.php
│ ├── Standard
│ │ ├── Exception
│ │ │ └── StrictViolationException.php
│ │ ├── Collection
│ │ │ ├── PdoCollection.php
│ │ │ └── CallbackCollection.php
│ │ ├── CsvFileObject.php
│ │ ├── Exporter.php
│ │ └── ExporterConfig.php
│ └── Protocol
│ │ ├── Exception
│ │ └── IOException.php
│ │ └── ExporterInterface.php
│ └── TestHelper
│ └── DbManager.php
├── example
├── temperature.tsv
├── user.csv
├── insert-or-update-user-for-mysql.php
├── export_from_database_via_pdo.php
├── tsv-sample.php
├── import_from_database_via_pdo.php
└── sample.php
├── .gitignore
├── scripts
└── bundle-devtools.sh
├── .travis.yml
├── phpunit-bootstrap.php
├── LICENSE
├── composer.json
├── phpunit.xml.dist
└── README.md
/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/mac-excel.csv:
--------------------------------------------------------------------------------
1 | a,b,c
d,e,f
--------------------------------------------------------------------------------
/example/temperature.tsv:
--------------------------------------------------------------------------------
1 | 9 Tokyo
2 | 27 Singapore
3 | -5 Seoul
4 | 7 Shanghai
5 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Join/csv_files/utf-8.csv:
--------------------------------------------------------------------------------
1 | ✔,✔,✔
2 | ★,★,★
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .phpmake
2 | /vendor
3 | /composer.phar
4 | /composer.lock
5 | /metrics
6 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/utf-8.csv:
--------------------------------------------------------------------------------
1 | ✔,✔,✔,✔
2 | ★,★,★,★
3 | 유,니,코,드
--------------------------------------------------------------------------------
/example/user.csv:
--------------------------------------------------------------------------------
1 | 1,alice,alice@example.com
2 | 2,bob,bob@example.com
3 | 3,carol,carol@eample.com
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/colon-separated.csv:
--------------------------------------------------------------------------------
1 | value1:value2:value3
2 | value4:value5:value6
3 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/tab-separated.csv:
--------------------------------------------------------------------------------
1 | value1 value2 value3
2 | value4 value5 value6
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/sjis.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodby/csv/HEAD/src/Goodby/CSV/Import/Tests/Standard/Join/csv_files/sjis.csv
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Join/csv_files/euc-jp.csv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goodby/csv/HEAD/src/Goodby/CSV/Export/Tests/Standard/Join/csv_files/euc-jp.csv
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/Exception/StrictViolationException.php:
--------------------------------------------------------------------------------
1 | data = $data;
13 | }
14 |
15 | public function getIterator()
16 | {
17 | return new ArrayIterator($this->data);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Protocol/ExporterInterface.php:
--------------------------------------------------------------------------------
1 | query('CREATE TABLE IF NOT EXISTS user2 (id INT, `name` VARCHAR(255), email VARCHAR(255), PRIMARY KEY (`id`))');
11 |
12 | $config = new LexerConfig();
13 | $lexer = new Lexer($config);
14 |
15 | $interpreter = new Interpreter();
16 |
17 | $interpreter->addObserver(function(array $columns) use ($pdo) {
18 | $stmt = $pdo->prepare('REPLACE user2 (id, name, email) VALUES (?, ?, ?)');
19 | $stmt->execute($columns);
20 | });
21 |
22 | $lexer->parse('user.csv', $interpreter);
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | matrix:
4 | include:
5 | - php: 5.3
6 | dist: precise
7 | - php: 5.4
8 | dist: trusty
9 | - php: 5.5
10 | dist: trusty
11 | - php: 5.6
12 | dist: trusty
13 | - php: 7.0
14 | dist: trusty
15 | - php: 7.1
16 | dist: trusty
17 | # PHP 7.2 can currently not been tested because of incompatibility with PHPUnit 3
18 | # atleast PHPUnit 7.0 would be needed which don't support 5.3
19 | # - php: 7.2
20 | # dist: trusty
21 |
22 | before_script:
23 | - ./scripts/bundle-devtools.sh .
24 | - export GOODBY_CSV_TEST_DB_HOST=127.0.0.1
25 | - export GOODBY_CSV_TEST_DB_USER=root
26 | - export GOODBY_CSV_TEST_DB_PASS=""
27 | - mysql -e 'create database goodby_csv_test'
28 | script: ./vendor/bin/phpunit --coverage-text --configuration phpunit.xml.dist
29 |
--------------------------------------------------------------------------------
/example/export_from_database_via_pdo.php:
--------------------------------------------------------------------------------
1 | query('CREATE TABLE IF NOT EXISTS user (id INT, `name` VARCHAR(255), email VARCHAR(255))');
13 | $pdo->query("INSERT INTO user VALUES(1, 'alice', 'alice@example.com')");
14 | $pdo->query("INSERT INTO user VALUES(2, 'bob', 'bob@example.com')");
15 | $pdo->query("INSERT INTO user VALUES(3, 'carol', 'carol@example.com')");
16 |
17 | $config = new ExporterConfig();
18 | $exporter = new Exporter($config);
19 |
20 | $stmt = $pdo->prepare("SELECT * FROM user");
21 | $stmt->execute();
22 |
23 | $exporter->export('php://output', new PdoCollection($stmt));
24 |
25 |
--------------------------------------------------------------------------------
/example/tsv-sample.php:
--------------------------------------------------------------------------------
1 | setDelimiter("\t");
15 | $config->setFlags(\SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY | \SplFileObject::READ_CSV);
16 | $lexer = new Lexer($config);
17 |
18 | // set up interpreter
19 | $interpreter = new Interpreter();
20 | $interpreter->addObserver(function(array $row) use (&$temperature) {
21 | $temperature[] = array(
22 | 'temperature' => $row[0],
23 | 'city' => $row[1],
24 | );
25 | });
26 |
27 | // parse
28 | $lexer->parse('temperature.tsv', $interpreter);
29 |
30 | var_dump($temperature);
31 |
--------------------------------------------------------------------------------
/example/import_from_database_via_pdo.php:
--------------------------------------------------------------------------------
1 | query('CREATE TABLE IF NOT EXISTS user (id INT, `name` VARCHAR(255), email VARCHAR(255))');
11 |
12 | $config = new LexerConfig();
13 | $lexer = new Lexer($config);
14 |
15 | $interpreter = new Interpreter();
16 |
17 | $interpreter->addObserver(function(array $columns) use ($pdo) {
18 | $checkStmt = $pdo->prepare('SELECT count(*) FROM user WHERE id = ?');
19 | $checkStmt->execute(array(($columns[0])));
20 |
21 | $count = $checkStmt->fetchAll()[0][0];
22 |
23 | if ($count === 0) {
24 | $stmt = $pdo->prepare('INSERT INTO user (id, name, email) VALUES (?, ?, ?)');
25 | $stmt->execute($columns);
26 | }
27 | });
28 |
29 | $lexer->parse('user.csv', $interpreter);
30 |
--------------------------------------------------------------------------------
/phpunit-bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.3.2",
23 | "ext-mbstring": "*"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "3.7.*",
27 | "mockery/mockery": "^0.7.2",
28 | "suin/php-expose": "^1.0",
29 | "mikey179/vfsStream": "^1.1"
30 | },
31 | "autoload": {
32 | "psr-0": {
33 | "Goodby\\CSV": "src/"
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/sample.php:
--------------------------------------------------------------------------------
1 | PDO::ERRMODE_EXCEPTION,
13 | ));
14 | $pdo->query('CREATE TABLE IF NOT EXISTS user (id INT, `name` VARCHAR(255), email VARCHAR(255))');
15 |
16 | // Importing
17 | $config = new LexerConfig();
18 | $lexer = new Lexer($config);
19 | $interpreter = new Interpreter();
20 | $interpreter->addObserver(function(array $columns) use ($pdo) {
21 | $stmt = $pdo->prepare('INSERT INTO user (id, name, email) VALUES (?, ?, ?)');
22 | $stmt->execute($columns);
23 | });
24 | $lexer->parse('user.csv', $interpreter);
25 |
26 | // Exporting
27 | $config = new ExporterConfig();
28 | $exporter = new Exporter($config);
29 | $exporter->export('php://output', array(
30 | array('1', 'alice', 'alice@example.com'),
31 | array('2', 'bob', 'bob@example.com'),
32 | array('3', 'carol', 'carol@example.com'),
33 | ));
34 |
35 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Protocol/LexerTest.php:
--------------------------------------------------------------------------------
1 | shouldReceive('parse')->with($path, $interpreter);
20 |
21 | $lexer->parse($path, $interpreter);
22 | }
23 |
24 | /**
25 | * @expectedException \Goodby\CSV\Import\Protocol\Exception\CsvFileNotFoundException
26 | */
27 | public function testCsvFileNotFound()
28 | {
29 | $lexer = m::mock('\Goodby\CSV\Import\Protocol\LexerInterface');
30 | $interpreter = m::mock('\Goodby\CSV\Import\Protocol\InterpreterInterface');
31 |
32 | $path = 'invalid_dummy.csv';
33 |
34 | $lexer->shouldReceive('parse')
35 | ->with($path, $interpreter)
36 | ->andThrow('Goodby\CSV\Import\Protocol\Exception\CsvFileNotFoundException')
37 | ;
38 |
39 | $lexer->parse($path, $interpreter);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Protocol/InterpreterTest.php:
--------------------------------------------------------------------------------
1 | getMock('\Goodby\CSV\Import\Protocol\InterpreterInterface');
17 |
18 | $interpreter->expects($this->once())
19 | ->method('interpret')
20 | ->with($this->identicalTo($line))
21 | ;
22 |
23 | $interpreter->interpret($line);
24 | }
25 |
26 | /**
27 | * @expectedException \Goodby\CSV\Import\Protocol\Exception\InvalidLexicalException
28 | */
29 | public function testInterpreterInterfaceWillThrownInvalidLexicalException()
30 | {
31 | $interpreter = $this->getMock('\Goodby\CSV\Import\Protocol\InterpreterInterface');
32 |
33 | $interpreter->expects($this->once())
34 | ->method('interpret')
35 | ->will($this->throwException(new InvalidLexicalException()))
36 | ;
37 |
38 | $line = "INVALID LEXICAL";
39 |
40 | $interpreter->interpret($line);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/SandboxDirectoryManager.php:
--------------------------------------------------------------------------------
1 | getMock('Goodby\CSV\Export\Protocol\ExporterInterface');
12 | $exporter->expects($this->once())->method('export');
13 |
14 | $exporter->export('filename', array(
15 | array('ID', 'name', 'email'),
16 | array('1', 'alice', 'alice@example.com'),
17 | array('2', 'bob', 'bob@example.com'),
18 | ));
19 | }
20 |
21 | /**
22 | * @expectedException \Goodby\CSV\Export\Protocol\Exception\IOException
23 | */
24 | public function testExportsThrowsIOException()
25 | {
26 | $exporter = $this->getMock('Goodby\CSV\Export\Protocol\ExporterInterface');
27 |
28 | $exporter
29 | ->expects($this->once())
30 | ->method('export')
31 | ->will($this->throwException(new IOException('Unable to write')));
32 |
33 | $exporter->export('/path/to/file.csv', array(
34 | array('ID', 'name', 'email'),
35 | array('1', 'alice', 'alice@example.com'),
36 | array('2', 'bob', 'bob@example.com'),
37 | ));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/TestHelper/DbManager.php:
--------------------------------------------------------------------------------
1 | host = $_SERVER['GOODBY_CSV_TEST_DB_HOST'];
17 | $this->db = $_SERVER['GOODBY_CSV_TEST_DB_NAME_DEFAULT'];
18 | $this->user = $_SERVER['GOODBY_CSV_TEST_DB_USER'];
19 | $this->pass = $_SERVER['GOODBY_CSV_TEST_DB_PASS'];
20 |
21 | $dsn = 'mysql:host=' . $this->host;
22 |
23 | $this->pdo = new \PDO($dsn, $this->user, $this->pass);
24 | $stmt = $this->pdo->prepare("CREATE DATABASE " . $this->db);
25 |
26 | $stmt->execute();
27 | }
28 |
29 | public function __destruct()
30 | {
31 | $stmt = $this->pdo->prepare("DROP DATABASE " . $this->db);
32 | $stmt->execute();
33 | }
34 |
35 | public function getPdo()
36 | {
37 | return new \PDO($this->getDsn(), $this->user, $this->pass);
38 | }
39 |
40 | public function getDsn()
41 | {
42 | return 'mysql:dbname=' . $this->db . ';host=' . $this->host;
43 | }
44 |
45 | public function getUser()
46 | {
47 | return $this->user;
48 | }
49 |
50 | public function getPassword()
51 | {
52 | return $this->pass;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Unit/Collection/CallbackCollectionTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($each[0], 'user');
24 | $this->assertEquals($each[1], 'name' . $index);
25 | $index++;
26 | }
27 | }
28 |
29 | public function testIteratorAggregate()
30 | {
31 |
32 | $data = array();
33 | $data[] = array('user', 'name1');
34 | $data[] = array('user', 'name2');
35 | $data[] = array('user', 'name3');
36 |
37 | $iterator = new SampleAggIterator($data);
38 |
39 | $collection = new CallbackCollection($iterator, function($mixed) {
40 | return $mixed;
41 | });
42 |
43 | $index = 1;
44 | foreach ($collection as $each) {
45 | $this->assertEquals($each[0], 'user');
46 | $this->assertEquals($each[1], 'name' . $index);
47 | $index++;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Unit/Observer/SqlObserverTest.php:
--------------------------------------------------------------------------------
1 | addObserver(array($sqlObserver, 'notify'));
30 |
31 | $interpreter->interpret(array('123', 'test', '28', 'true', 'false', 'null', 'test"test'));
32 |
33 | $expectedSql = 'INSERT INTO test(id, name, age, flag, flag2, status, contents) VALUES(123, "test", 28, true, false, NULL, "test\"test");';
34 |
35 | $this->assertEquals($expectedSql, file_get_contents($path));
36 | }
37 |
38 | /**
39 | * @expectedException \InvalidArgumentException
40 | */
41 | public function testInvalidLine()
42 | {
43 | $interpreter = new Interpreter();
44 |
45 | $sqlObserver = new SqlObserver('test', array('id', 'name'), 'dummy');
46 |
47 | $interpreter->addObserver(array($sqlObserver, 'notify'));
48 |
49 | $interpreter->interpret(array('123', array('test')));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/Observer/SqlObserver.php:
--------------------------------------------------------------------------------
1 | table = $table;
16 | $this->columns = $columns;
17 | $this->path = $path;
18 | }
19 |
20 | public function notify($line)
21 | {
22 | $sql = $this->buildSql($line);
23 |
24 | if ($this->file === null) {
25 | $this->file = new \SplFileObject($this->path, 'a');
26 | }
27 |
28 | $this->file->fwrite($sql);
29 | }
30 |
31 | private function buildSql($line)
32 | {
33 | $line = array_map(function($value) {
34 | $number = filter_var($value, FILTER_VALIDATE_INT);
35 |
36 | if ($number !== false) {
37 | return $number;
38 | }
39 |
40 | if (is_string($value)) {
41 | if (strtolower($value) === 'null') {
42 | return 'NULL';
43 | }
44 |
45 | if (strtolower($value) === 'true') {
46 | return 'true';
47 | }
48 |
49 | if (strtolower($value) === 'false') {
50 | return 'false';
51 | }
52 |
53 | return '"' . addslashes($value) . '"';
54 | }
55 |
56 | throw new \InvalidArgumentException('value is invalid: ' . var_export($value, 1));
57 | }, $line);
58 |
59 | return 'INSERT INTO ' . $this->table . '(' . join(', ', $this->columns) . ')' .
60 | ' VALUES(' . join(', ', $line) . ');';
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/CSVFiles.php:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | src/*/*/*/Tests
11 |
12 |
13 |
14 |
15 | ./../html/install/src
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 |
37 | src
38 |
39 |
40 |
41 | src
42 | src/*/*/*/Tests
43 |
44 |
45 |
46 |
47 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/Observer/PdoObserver.php:
--------------------------------------------------------------------------------
1 | table = $table;
18 | $this->columns = $columns;
19 |
20 | $this->dsn = $dsn;
21 | $this->options = $options;
22 | }
23 |
24 | public function notify($line)
25 | {
26 | if ($this->pdo === null) {
27 | $this->pdo = new \PDO($this->dsn, $this->options['user'], $this->options['password']);
28 | }
29 |
30 | $this->execute($line);
31 | }
32 |
33 | private function execute($line)
34 | {
35 | $line = array_map(function($value) {
36 | $number = filter_var($value, FILTER_VALIDATE_INT);
37 |
38 | if ($number !== false) {
39 | return $number;
40 | }
41 |
42 | if (is_string($value)) {
43 | if (strtolower($value) === 'null') {
44 | return 'NULL';
45 | }
46 |
47 | if (strtolower($value) === 'true') {
48 | return 1;
49 | }
50 |
51 | if (strtolower($value) === 'false') {
52 | return 0;
53 | }
54 |
55 | return $value;
56 | }
57 |
58 | throw new \InvalidArgumentException('value is invalid: ' . var_export($value, 1));
59 | }, $line);
60 |
61 | $prepare = array_map(function() {
62 | return '?';
63 | }, $line);
64 |
65 | $sql = 'INSERT INTO ' . $this->table . '(' . join(', ', $this->columns) . ')' .
66 | ' VALUES(' . join(',', $prepare) . ')';
67 |
68 | $stmt = $this->pdo->prepare($sql);
69 | $stmt->execute($line);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Join/Collection/PdoCollectionTest.php:
--------------------------------------------------------------------------------
1 | manager = new DbManager();
20 |
21 | $pdo = $this->manager->getPdo();
22 |
23 | $stmt = $pdo->prepare("CREATE TABLE collection_test ( id INT, name VARCHAR(32) )");
24 | $stmt->execute();
25 |
26 | $pdo->prepare("INSERT INTO collection_test VALUES(1, 'name')")->execute();
27 | $pdo->prepare("INSERT INTO collection_test VALUES(2, 'name')")->execute();
28 | $pdo->prepare("INSERT INTO collection_test VALUES(3, 'name')")->execute();
29 | }
30 |
31 | public function tearDown()
32 | {
33 | unset($this->manager);
34 | }
35 |
36 | public function testUsage()
37 | {
38 | $pdo = $this->manager->getPdo();
39 |
40 | $stmt = $pdo->prepare("SELECT * FROM collection_test");
41 | $stmt->execute();
42 |
43 | $pdoCollection = new PdoCollection($stmt);
44 |
45 | foreach ($pdoCollection as $line) {
46 | $this->assertEquals("name", $line["name"]);
47 | }
48 | }
49 |
50 | public function testUsageWithCallbackCollection()
51 | {
52 | $pdo = $this->manager->getPdo();
53 |
54 | $stmt = $pdo->prepare("SELECT * FROM collection_test");
55 | $stmt->execute();
56 |
57 | $pdoCollection = new PdoCollection($stmt);
58 |
59 | $callbackCollection = new CallbackCollection($pdoCollection, function($row) {
60 | $row['test'] = 'test';
61 | return $row;
62 | });
63 |
64 | foreach ($callbackCollection as $line) {
65 | $this->assertEquals('test', $line['test']);
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Unit/ExporterConfigTest.php:
--------------------------------------------------------------------------------
1 | assertSame(',', $config->getDelimiter());
13 | $this->assertSame('del', $config->setDelimiter('del')->getDelimiter());
14 | }
15 |
16 | public function testEnclosure()
17 | {
18 | $config = new ExporterConfig();
19 | $this->assertSame('"', $config->getEnclosure());
20 | $this->assertSame('enc', $config->setEnclosure('enc')->getEnclosure());
21 | }
22 |
23 | public function testEscape()
24 | {
25 | $config = new ExporterConfig();
26 | $this->assertSame('\\', $config->getEscape());
27 | $this->assertSame('esc', $config->setEscape('esc')->getEscape());
28 | }
29 |
30 | public function testNewline()
31 | {
32 | $config = new ExporterConfig();
33 | $this->assertSame("\r\n", $config->getNewline());
34 | $this->assertSame("\r", $config->setNewline("\r")->getNewline());
35 | }
36 |
37 | public function testFromCharset()
38 | {
39 | $config = new ExporterConfig();
40 | $this->assertSame('auto', $config->getFromCharset());
41 | $this->assertSame('UTF-8', $config->setFromCharset('UTF-8')->getFromCharset());
42 | }
43 |
44 | public function testToCharset()
45 | {
46 | $config = new ExporterConfig();
47 | $this->assertSame(null, $config->getToCharset());
48 | $this->assertSame('UTF-8', $config->setToCharset('UTF-8')->getToCharset());
49 | }
50 |
51 | public function testColumnHeaders()
52 | {
53 | $columnHeaders = array(
54 | 'Header 1',
55 | 'Header 2',
56 | 'Header 3',
57 | );
58 |
59 | $config = new ExporterConfig();
60 | $this->assertSame(array(), $config->getColumnHeaders());
61 | $this->assertSame($columnHeaders, $config->setColumnHeaders($columnHeaders)->getColumnHeaders());
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/Collection/PdoCollection.php:
--------------------------------------------------------------------------------
1 | stmt = $stmt;
22 |
23 | $this->rowCount = $this->stmt->rowCount();
24 | }
25 |
26 | /**
27 | * (PHP 5 >= 5.0.0)
28 | * Return the current element
29 | * @link http://php.net/manual/en/iterator.current.php
30 | * @return mixed Can return any type.
31 | */
32 | public function current()
33 | {
34 | return $this->stmt->fetch(PDO::FETCH_ASSOC);
35 | }
36 |
37 | /**
38 | * (PHP 5 >= 5.0.0)
39 | * Move forward to next element
40 | * @link http://php.net/manual/en/iterator.next.php
41 | * @return void Any returned value is ignored.
42 | */
43 | public function next()
44 | {
45 | $this->current++;
46 | }
47 |
48 | /**
49 | * (PHP 5 >= 5.0.0)
50 | * Return the key of the current element
51 | * @link http://php.net/manual/en/iterator.key.php
52 | * @return mixed scalar on success, or null on failure.
53 | */
54 | public function key()
55 | {
56 | $this->current;
57 | }
58 |
59 | /**
60 | * (PHP 5 >= 5.0.0)
61 | * Checks if current position is valid
62 | * @link http://php.net/manual/en/iterator.valid.php
63 | * @return boolean The return value will be casted to boolean and then evaluated.
64 | * Returns true on success or false on failure.
65 | */
66 | public function valid()
67 | {
68 | return ($this->rowCount > $this->current);
69 | }
70 |
71 | /**
72 | * (PHP 5 >= 5.0.0)
73 | * Rewind the Iterator to the first element
74 | * @link http://php.net/manual/en/iterator.rewind.php
75 | * @return void Any returned value is ignored.
76 | */
77 | public function rewind()
78 | {
79 | $this->stmt->execute();
80 | $this->current = 0;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Unit/LexerConfigTest.php:
--------------------------------------------------------------------------------
1 | assertSame(',', $config->getDelimiter());
14 | $config->setDelimiter('del');
15 | $this->assertSame('del', $config->getDelimiter());
16 | }
17 |
18 | public function testEnclosure()
19 | {
20 | $config = new LexerConfig();
21 | $this->assertSame('"', $config->getEnclosure());
22 | $this->assertSame('enc', $config->setEnclosure('enc')->getEnclosure());
23 | }
24 |
25 | public function testEscape()
26 | {
27 | $config = new LexerConfig();
28 | $this->assertSame('\\', $config->getEscape());
29 | $this->assertSame('esc', $config->setEscape('esc')->getEscape());
30 | }
31 |
32 | public function testFromCharset()
33 | {
34 | $config = new LexerConfig();
35 | $this->assertSame(null, $config->getFromCharset());
36 | $this->assertSame('UTF-8', $config->setFromCharset('UTF-8')->getFromCharset());
37 | }
38 |
39 | public function testToCharset()
40 | {
41 | $config = new LexerConfig();
42 | $this->assertSame(null, $config->getToCharset());
43 | $this->assertSame('UTF-8', $config->setToCharset('UTF-8')->getToCharset());
44 | }
45 |
46 | public function testFlags()
47 | {
48 | $config = new LexerConfig();
49 | $this->assertSame(SplFileObject::READ_CSV, $config->getFlags());
50 | $config->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::READ_CSV);
51 | $flags = (SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::READ_CSV);
52 | $this->assertSame($flags, $config->getFlags());
53 | }
54 |
55 | public function testIgnoreHeaderLine()
56 | {
57 | $config = new LexerConfig();
58 | $this->assertSame(false, $config->getIgnoreHeaderLine());
59 | $this->assertSame(true, $config->setIgnoreHeaderLine(true)->getIgnoreHeaderLine());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Unit/StreamFilter/ConvertMbstringEncodingTest.php:
--------------------------------------------------------------------------------
1 | internalEncodingBackup = mb_internal_encoding();
15 | }
16 |
17 | public function tearDown()
18 | {
19 | mb_internal_encoding($this->internalEncodingBackup);
20 | }
21 |
22 | public function testGetFilterName()
23 | {
24 | $this->assertSame('convert.mbstring.encoding.*', ConvertMbstringEncoding::getFilterName());
25 | }
26 |
27 | public function testOneParameter()
28 | {
29 | $filterString = 'convert.mbstring.encoding.EUC-JP';
30 | mb_internal_encoding('UTF-7');
31 | $filter = new ConvertMbstringEncoding();
32 | $filter->filtername = $filterString;
33 | $filter->onCreate();
34 | $this->assertAttributeSame('EUC-JP', 'fromCharset', $filter);
35 | $this->assertAttributeSame('UTF-7', 'toCharset', $filter);
36 | }
37 |
38 | public function testTwoParameters()
39 | {
40 | $filterString = 'convert.mbstring.encoding.SJIS-win:UTF-8';
41 | mb_internal_encoding('UTF-7');
42 | $filter = new ConvertMbstringEncoding();
43 | $filter->filtername = $filterString;
44 | $filter->onCreate();
45 | $this->assertAttributeSame('SJIS-win', 'fromCharset', $filter);
46 | $this->assertAttributeSame('UTF-8', 'toCharset', $filter);
47 | }
48 |
49 | public function test_when_invalid_parameter_given_it_returns_false()
50 | {
51 | $filterString = 'convert.mbstring.encoding.@#$#!%^^';
52 | $filter = new ConvertMbstringEncoding();
53 | $filter->filtername = $filterString;
54 | $this->assertFalse($filter->onCreate());
55 | }
56 |
57 | public function test_register_filter()
58 | {
59 | ConvertMbstringEncoding::register();
60 | $filterName = ConvertMbstringEncoding::getFilterName();
61 | $registeredFilters = stream_get_filters();
62 | $this->assertTrue(in_array($filterName, $registeredFilters));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/Lexer.php:
--------------------------------------------------------------------------------
1 | config = $config;
28 | ConvertMbstringEncoding::register();
29 | }
30 |
31 | /**
32 | * {@inherit}
33 | */
34 | public function parse($filename, InterpreterInterface $interpreter)
35 | {
36 | ini_set('auto_detect_line_endings', true); // For mac's office excel csv
37 |
38 | $delimiter = $this->config->getDelimiter();
39 | $enclosure = $this->config->getEnclosure();
40 | $escape = $this->config->getEscape();
41 | $fromCharset = $this->config->getFromCharset();
42 | $toCharset = $this->config->getToCharset();
43 | $flags = $this->config->getFlags();
44 | $ignoreHeader = $this->config->getIgnoreHeaderLine();
45 |
46 | if ( $fromCharset === null ) {
47 | $url = $filename;
48 | } else {
49 | $url = ConvertMbstringEncoding::getFilterURL($filename, $fromCharset, $toCharset);
50 | }
51 |
52 | $csv = new SplFileObject($url);
53 | $csv->setCsvControl($delimiter, $enclosure, $escape);
54 | $csv->setFlags($flags);
55 |
56 | $originalLocale = setlocale(LC_ALL, '0'); // Backup current locale
57 | setlocale(LC_ALL, 'en_US.UTF-8');
58 |
59 | foreach ( $csv as $lineNumber => $line ) {
60 | if ($ignoreHeader && $lineNumber == 0 || (count($line) === 1 && trim($line[0]) === '')) {
61 | continue;
62 | }
63 | $interpreter->interpret($line);
64 | }
65 |
66 | parse_str(str_replace(';', '&', $originalLocale), $locale_array);
67 | setlocale(LC_ALL, $locale_array); // Reset locale
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/CsvFileObject.php:
--------------------------------------------------------------------------------
1 | newline = $newline;
31 | }
32 |
33 | /**
34 | * Set csv filter
35 | * @param callable $filter
36 | */
37 | public function setCsvFilter($filter)
38 | {
39 | $this->csvFilter = $filter;
40 | }
41 |
42 | /**
43 | * Write a field array as a CSV line
44 | * @param array $fields
45 | * @param string $delimiter
46 | * @param string $enclosure
47 | * @param useless $escape THIS PARAM IS UNSED, BUT REQUIRED EXISTS, see https://bugs.php.net/bug.php?id=68479 and https://github.com/goodby/csv/issues/56
48 | * @return int|void
49 | */
50 | public function fputcsv($fields, $delimiter = null, $enclosure = null, $escape = null)
51 | {
52 | // Temporary output a line to memory to get line as string
53 | $fp = fopen('php://temp', 'w+');
54 | $arguments = func_get_args();
55 | array_unshift($arguments, $fp);
56 | call_user_func_array('fputcsv', $arguments);
57 | rewind($fp);
58 |
59 | $line = '';
60 |
61 | while ( feof($fp) === false ) {
62 | $line .= fgets($fp);
63 | }
64 |
65 | fclose($fp);
66 |
67 | /**
68 | * Because the php_fputcsv() implementation in PHP´s source code
69 | * has a hardcoded "\n", this method replaces the last LF code
70 | * with what the client code wishes.
71 | */
72 | $line = rtrim($line, "\n"). $this->newline;
73 |
74 | // if the enclosure was '' | false
75 | if (empty($enclosure)) {
76 | $line = str_replace("\0", '', $line);
77 | }
78 |
79 | if ( is_callable($this->csvFilter) ) {
80 | $line = call_user_func($this->csvFilter, $line);
81 | }
82 |
83 | return $this->fwrite($line);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/Collection/CallbackCollection.php:
--------------------------------------------------------------------------------
1 | callable = $callable;
16 |
17 | if (!is_callable($callable)) {
18 | throw new \InvalidArgumentException('the second argument must be callable');
19 | }
20 |
21 | if (is_array($data)) {
22 | $ao = new \ArrayObject($data);
23 | $this->data = $ao->getIterator();
24 | } elseif ($data instanceof Iterator) {
25 | $this->data = $data;
26 | } elseif ($data instanceof IteratorAggregate) {
27 | $this->data = $data->getIterator();
28 | } else {
29 | throw new \InvalidArgumentException('data must be an array or an Iterator/IteratorAggregate');
30 | }
31 | }
32 |
33 | /**
34 | * (PHP 5 >= 5.0.0)
35 | * Return the current element
36 | * @link http://php.net/manual/en/iterator.current.php
37 | * @return mixed Can return any type.
38 | */
39 | public function current()
40 | {
41 | return call_user_func($this->callable, $this->data->current());
42 | }
43 |
44 | /**
45 | * (PHP 5 >= 5.0.0)
46 | * Move forward to next element
47 | * @link http://php.net/manual/en/iterator.next.php
48 | * @return void Any returned value is ignored.
49 | */
50 | public function next()
51 | {
52 | $this->data->next();
53 | }
54 |
55 | /**
56 | * (PHP 5 >= 5.0.0)
57 | * Return the key of the current element
58 | * @link http://php.net/manual/en/iterator.key.php
59 | * @return mixed scalar on success, or null on failure.
60 | */
61 | public function key()
62 | {
63 | return $this->data->key();
64 | }
65 |
66 | /**
67 | * (PHP 5 >= 5.0.0)
68 | * Checks if current position is valid
69 | * @link http://php.net/manual/en/iterator.valid.php
70 | * @return boolean The return value will be casted to boolean and then evaluated.
71 | * Returns true on success or false on failure.
72 | */
73 | public function valid()
74 | {
75 | return $this->data->valid();
76 | }
77 |
78 | /**
79 | * (PHP 5 >= 5.0.0)
80 | * Rewind the Iterator to the first element
81 | * @link http://php.net/manual/en/iterator.rewind.php
82 | * @return void Any returned value is ignored.
83 | */
84 | public function rewind()
85 | {
86 | $this->data->rewind();
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/Observer/PdoObserverTest.php:
--------------------------------------------------------------------------------
1 | manager = new DbManager();
25 |
26 | $pdo = $this->manager->getPdo();
27 |
28 | $stmt = $pdo->prepare("CREATE TABLE test (id INT, name VARCHAR(32), age INT, flag TINYINT, flag2 TINYINT, status VARCHAR(32), contents TEXT)");
29 | $stmt->execute();
30 | }
31 |
32 | public function tearDown()
33 | {
34 | unset($this->manager);
35 | }
36 |
37 | public function testUsage()
38 | {
39 | $interpreter = new Interpreter();
40 |
41 | $table = 'test';
42 |
43 | $dsn = $this->manager->getDsn();
44 | $options = array('user' => $this->manager->getUser(), 'password' => $this->manager->getPassword());
45 |
46 | $sqlObserver = new PdoObserver($table, array('id', 'name', 'age', 'flag', 'flag2', 'status', 'contents'), $dsn, $options);
47 |
48 | $interpreter->addObserver(array($sqlObserver, 'notify'));
49 |
50 | $interpreter->interpret(array('123', 'test', '28', 'true', 'false', 'null', 'test"test'));
51 |
52 | $pdo = $this->manager->getPdo();
53 |
54 | $stmt = $pdo->prepare("SELECT * FROM " . $table);
55 | $stmt->execute();
56 |
57 | $result = $stmt->fetch();
58 |
59 | $this->assertEquals(123, $result[0]);
60 | $this->assertEquals('test', $result[1]);
61 | $this->assertEquals(28, $result[2]);
62 | $this->assertEquals(1, $result[3]);
63 | $this->assertEquals(0, $result[4]);
64 | $this->assertEquals('NULL', $result[5]);
65 | $this->assertEquals('test"test', $result[6]);
66 | }
67 |
68 | /**
69 | * @expectedException \InvalidArgumentException
70 | * @expectedExceptionMessage value is invalid: array
71 | */
72 | public function testInvalidLine()
73 | {
74 | $interpreter = new Interpreter();
75 |
76 | $table = 'test';
77 |
78 | $options = array('user' => $this->manager->getUser(), 'password' => $this->manager->getPassword());
79 |
80 | $sqlObserver = new PdoObserver($table, array('id', 'name'), $this->manager->getDsn(), $options);
81 |
82 | $interpreter->addObserver(array($sqlObserver, 'notify'));
83 |
84 | $interpreter->interpret(array('123', array('test', 'test')));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Join/UsageTest.php:
--------------------------------------------------------------------------------
1 | root = vfsStream::setup('output');
31 |
32 | $this->manager = new DbManager();
33 |
34 | $pdo = $this->manager->getPdo();
35 |
36 | $stmt = $pdo->prepare("CREATE TABLE collection_test ( id INT, name VARCHAR(32) )");
37 | $stmt->execute();
38 |
39 | $pdo->prepare("INSERT INTO collection_test VALUES(1, 'name')")->execute();
40 | $pdo->prepare("INSERT INTO collection_test VALUES(2, 'name')")->execute();
41 | $pdo->prepare("INSERT INTO collection_test VALUES(3, 'name')")->execute();
42 | }
43 |
44 | public function tearDown()
45 | {
46 | unset($this->manager);
47 | }
48 |
49 | public function testUsage()
50 | {
51 | $pdo = $this->manager->getPdo();
52 |
53 | $stmt = $pdo->prepare("SELECT * FROM collection_test");
54 | $stmt->execute();
55 |
56 | $this->assertFileNotExists('vfs://output/data.csv');
57 |
58 | $collection = new PdoCollection($stmt);
59 |
60 | $config = new ExporterConfig();
61 | $exporter = new Exporter($config);
62 | $exporter->export('vfs://output/data.csv', $collection);
63 |
64 | $expectedContents = "1,name\r\n";
65 | $expectedContents .= "2,name\r\n";
66 | $expectedContents .= "3,name\r\n";
67 |
68 | $this->assertSame($expectedContents, file_get_contents('vfs://output/data.csv'));
69 | }
70 |
71 | public function testUsageWithCallbackCollection()
72 | {
73 | $this->assertFileNotExists('vfs://output/data.csv');
74 |
75 | $data = array();
76 | $data[] = array(1, 'name1');
77 | $data[] = array(2, 'name2');
78 | $data[] = array(3, 'name3');
79 |
80 | $collection = new CallbackCollection($data, function($row) {
81 | $row[1] = $row[1] . '!';
82 | return $row;
83 | });
84 |
85 | $config = new ExporterConfig();
86 | $exporter = new Exporter($config);
87 | $exporter->export('vfs://output/data.csv', $collection);
88 |
89 | $expectedContents = "1,name1!\r\n";
90 | $expectedContents .= "2,name2!\r\n";
91 | $expectedContents .= "3,name3!\r\n";
92 |
93 | $this->assertSame($expectedContents, file_get_contents('vfs://output/data.csv'));
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/Interpreter.php:
--------------------------------------------------------------------------------
1 | checkRowConsistency($line);
39 |
40 | if (!is_array($line)) {
41 | throw new InvalidLexicalException('line is must be array');
42 | }
43 |
44 | $this->notify($line);
45 | }
46 |
47 | public function unstrict()
48 | {
49 | $this->strict = false;
50 | }
51 |
52 | /**
53 | * add observer
54 | *
55 | * @param callable $observer
56 | */
57 | public function addObserver($observer)
58 | {
59 | $this->checkCallable($observer);
60 |
61 | $this->observers[] = $observer;
62 | }
63 |
64 | /**
65 | * notify to observers
66 | *
67 | * @param $line
68 | */
69 | private function notify($line)
70 | {
71 | $observers = $this->observers;
72 |
73 | foreach ($observers as $observer) {
74 | $this->delegate($observer, $line);
75 | }
76 | }
77 |
78 | /**
79 | * delegate to observer
80 | *
81 | * @param $observer
82 | * @param $line
83 | */
84 | private function delegate($observer, $line)
85 | {
86 | call_user_func($observer, $line);
87 | }
88 |
89 | /**
90 | * check observer is callable
91 | *
92 | * @param $observer
93 | * @throws \InvalidArgumentException
94 | */
95 | private function checkCallable($observer)
96 | {
97 | if (!is_callable($observer)) {
98 | throw new \InvalidArgumentException('observer must be callable');
99 | }
100 | }
101 |
102 | private function checkRowConsistency($line)
103 | {
104 | if (!$this->strict) {
105 | return;
106 | }
107 |
108 | $current = count($line);
109 |
110 | if ($this->rowConsistency === null) {
111 | $this->rowConsistency = $current;
112 | }
113 |
114 | if ($current !== $this->rowConsistency) {
115 | throw new StrictViolationException(sprintf('Column size should be %u, but %u columns given', $this->rowConsistency, $current));
116 | }
117 |
118 | $this->rowConsistency = $current;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/Exporter.php:
--------------------------------------------------------------------------------
1 | config = $config;
36 | }
37 |
38 | /**
39 | * Disable strict mode
40 | */
41 | public function unstrict()
42 | {
43 | $this->strict = false;
44 | }
45 |
46 | /**
47 | * {@inherit}
48 | * @throws StrictViolationException
49 | */
50 | public function export($filename, $rows)
51 | {
52 | $delimiter = $this->config->getDelimiter();
53 | $enclosure = $this->config->getEnclosure();
54 | $enclosure = empty($enclosure) ? "\0" : $enclosure;
55 | $newline = $this->config->getNewline();
56 | $fromCharset = $this->config->getFromCharset();
57 | $toCharset = $this->config->getToCharset();
58 | $fileMode = $this->config->getFileMode();
59 | $columnHeaders = $this->config->getColumnHeaders();
60 |
61 | try {
62 | $csv = new CsvFileObject($filename, $fileMode);
63 | } catch ( \Exception $e ) {
64 | throw new IOException($e->getMessage(), null, $e);
65 | }
66 |
67 | $csv->setNewline($newline);
68 |
69 | if ( $toCharset ) {
70 | $csv->setCsvFilter(function($line) use($toCharset, $fromCharset) {
71 | return mb_convert_encoding($line, $toCharset, $fromCharset);
72 | });
73 | }
74 |
75 | if (count($columnHeaders) > 0) {
76 | $this->checkRowConsistency($columnHeaders);
77 | $csv->fputcsv($columnHeaders, $delimiter, $enclosure);
78 | }
79 |
80 | foreach ( $rows as $row ) {
81 | $this->checkRowConsistency($row);
82 | $csv->fputcsv($row, $delimiter, $enclosure);
83 | }
84 | $csv->fflush();
85 | }
86 |
87 | /**
88 | * Check if the column count is consistent with comparing other rows
89 | * @param array|\Countable $row
90 | * @throws Exception\StrictViolationException
91 | */
92 | private function checkRowConsistency($row)
93 | {
94 | if ( $this->strict === false ) {
95 | return;
96 | }
97 |
98 | $current = count($row);
99 |
100 | if ( $this->rowConsistency === null ) {
101 | $this->rowConsistency = $current;
102 | }
103 |
104 | if ( $current !== $this->rowConsistency ) {
105 | throw new StrictViolationException();
106 | }
107 |
108 | $this->rowConsistency = $current;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/StreamFilter/ConvertMbstringEncoding.php:
--------------------------------------------------------------------------------
1 | filtername, self::FILTER_NAMESPACE) !== 0 ) {
78 | return false;
79 | }
80 |
81 | $parameterString = substr($this->filtername, strlen(self::FILTER_NAMESPACE));
82 |
83 | if ( ! preg_match('/^(?P[-\w]+)(:(?P[-\w]+))?$/', $parameterString, $matches) ) {
84 | return false;
85 | }
86 |
87 | $this->fromCharset = isset($matches['from']) ? $matches['from'] : 'auto';
88 | $this->toCharset = isset($matches['to']) ? $matches['to'] : mb_internal_encoding();
89 |
90 | return true;
91 | }
92 |
93 | /**
94 | * @param string $in
95 | * @param string $out
96 | * @param string $consumed
97 | * @param $closing
98 | * @return int
99 | */
100 | public function filter($in, $out, &$consumed, $closing)
101 | {
102 | while ( $bucket = stream_bucket_make_writeable($in) ) {
103 | $bucket->data = mb_convert_encoding($bucket->data, $this->toCharset, $this->fromCharset);
104 | $consumed += $bucket->datalen;
105 | stream_bucket_append($out, $bucket);
106 | }
107 |
108 | return PSFS_PASS_ON;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Unit/InterpreterTest.php:
--------------------------------------------------------------------------------
1 | expectedLine = null;
20 | }
21 |
22 | /**
23 | * @requires PHP 5.4
24 | */
25 | public function testStandardInterpreterWithClosure()
26 | {
27 | $this->expectedLine = array('test', 'test', 'test');
28 |
29 | $interpreter = new Interpreter();
30 | $interpreter->addObserver(function($line) {
31 | $this->assertEquals($this->expectedLine, $line);
32 | });
33 |
34 | $interpreter->interpret($this->expectedLine);
35 | }
36 |
37 | public function testStandardInterpreterWithObject()
38 | {
39 | $this->expectedLine = array('test', 'test', 'test');
40 |
41 | $object = m::mock('stdClass');
42 | $object->shouldReceive('callback')->with($this->expectedLine)->once();
43 |
44 | $interpreter = new Interpreter();
45 | $interpreter->addObserver(array($object, 'callback'));
46 |
47 | $interpreter->interpret($this->expectedLine);
48 | }
49 |
50 | /**
51 | * @expectedException \Goodby\CSV\Import\Standard\Exception\StrictViolationException
52 | */
53 | public function testInconsistentColumns()
54 | {
55 | $lines[] = array('test', 'test', 'test');
56 | $lines[] = array('test', 'test');
57 |
58 | $interpreter = new Interpreter();
59 |
60 | foreach ($lines as $line) {
61 | $interpreter->interpret($line);
62 | }
63 | }
64 |
65 | /**
66 | * @expectedException \Goodby\CSV\Import\Standard\Exception\StrictViolationException
67 | */
68 | public function testInconsistentColumnsLowToHigh()
69 | {
70 | $lines[] = array('test', 'test');
71 | $lines[] = array('test', 'test', 'test');
72 |
73 | $interpreter = new Interpreter();
74 |
75 | foreach ($lines as $line) {
76 | $interpreter->interpret($line);
77 | }
78 | }
79 |
80 | public function testConsistentColumns()
81 | {
82 | $lines[] = array('test', 'test', 'test');
83 | $lines[] = array('test', 'test', 'test');
84 |
85 | $interpreter = new Interpreter();
86 |
87 | foreach ($lines as $line) {
88 | $interpreter->interpret($line);
89 | }
90 | }
91 |
92 | /**
93 | * use un-strict won't throw exception with inconsistent columns
94 | *
95 | */
96 | public function testInconsistentColumnsWithUnStrict()
97 | {
98 | $lines[] = array('test', 'test', 'test');
99 | $lines[] = array('test', 'test');
100 |
101 | $interpreter = new Interpreter();
102 | $interpreter->unstrict();
103 |
104 | foreach ($lines as $line) {
105 | $interpreter->interpret($line);
106 | }
107 | }
108 |
109 | /**
110 | * @expectedException \Goodby\CSV\Import\Protocol\Exception\InvalidLexicalException
111 | */
112 | public function testStandardInterpreterWithInvalidLexical()
113 | {
114 | $this->expectedLine = '';
115 |
116 | $interpreter = new Interpreter();
117 |
118 | $interpreter->interpret($this->expectedLine);
119 | }
120 |
121 | /**
122 | * @expectedException \InvalidArgumentException
123 | */
124 | public function testInvalidCallable()
125 | {
126 | $interpreter = new Interpreter();
127 |
128 | $interpreter->addObserver('dummy');
129 |
130 | $interpreter->interpret($this->expectedLine);
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Standard/LexerConfig.php:
--------------------------------------------------------------------------------
1 | delimiter = $delimiter;
55 | return $this;
56 | }
57 |
58 | /**
59 | * Return delimiter
60 | * @return string
61 | */
62 | public function getDelimiter()
63 | {
64 | return $this->delimiter;
65 | }
66 |
67 | /**
68 | * Set enclosure
69 | * @param string $enclosure
70 | * @return LexerConfig
71 | */
72 | public function setEnclosure($enclosure)
73 | {
74 | $this->enclosure = $enclosure;
75 | return $this;
76 | }
77 |
78 | /**
79 | * Return enclosure
80 | * @return string
81 | */
82 | public function getEnclosure()
83 | {
84 | return $this->enclosure;
85 | }
86 |
87 | /**
88 | * Set escape
89 | * @param string $escape
90 | * @return LexerConfig
91 | */
92 | public function setEscape($escape)
93 | {
94 | $this->escape = $escape;
95 | return $this;
96 | }
97 |
98 | /**
99 | * Return escape
100 | * @return string
101 | */
102 | public function getEscape()
103 | {
104 | return $this->escape;
105 | }
106 |
107 | /**
108 | * Set from-character set
109 | * @param string $fromCharset
110 | * @return LexerConfig
111 | */
112 | public function setFromCharset($fromCharset)
113 | {
114 | $this->fromCharset = $fromCharset;
115 | return $this;
116 | }
117 |
118 | /**
119 | * Return from-character set
120 | * @return string
121 | */
122 | public function getFromCharset()
123 | {
124 | return $this->fromCharset;
125 | }
126 |
127 | /**
128 | * Set to-character set
129 | * @param string $toCharset
130 | * @return LexerConfig
131 | */
132 | public function setToCharset($toCharset)
133 | {
134 | $this->toCharset = $toCharset;
135 | return $this;
136 | }
137 |
138 | /**
139 | * Return to-character set
140 | * @return string
141 | */
142 | public function getToCharset()
143 | {
144 | return $this->toCharset;
145 | }
146 |
147 | /**
148 | * Set flags
149 | * @param integer $flags Bit mask of the flags to set. See SplFileObject constants for the available flags.
150 | * @return LexerConfig
151 | * @see http://php.net/manual/en/class.splfileobject.php#splfileobject.constants
152 | */
153 | public function setFlags($flags)
154 | {
155 | $this->flags = $flags;
156 | return $this;
157 | }
158 |
159 | /**
160 | * Return flags
161 | * @return integer
162 | */
163 | public function getFlags()
164 | {
165 | return $this->flags;
166 | }
167 |
168 | /**
169 | * @param $ignoreHeaderLine
170 | * @return $this
171 | */
172 | public function setIgnoreHeaderLine($ignoreHeaderLine)
173 | {
174 | $this->ignoreHeaderLine = $ignoreHeaderLine;
175 | return $this;
176 | }
177 |
178 | /**
179 | * @return boolean
180 | */
181 | public function getIgnoreHeaderLine()
182 | {
183 | return $this->ignoreHeaderLine;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Standard/ExporterConfig.php:
--------------------------------------------------------------------------------
1 | delimiter = $delimiter;
66 | return $this;
67 | }
68 |
69 | /**
70 | * Return delimiter
71 | * @return string
72 | */
73 | public function getDelimiter()
74 | {
75 | return $this->delimiter;
76 | }
77 |
78 | /**
79 | * Set enclosure
80 | * @param string $enclosure
81 | * @return ExporterConfig
82 | */
83 | public function setEnclosure($enclosure)
84 | {
85 | $this->enclosure = $enclosure;
86 | return $this;
87 | }
88 |
89 | /**
90 | * Return enclosure
91 | * @return string
92 | */
93 | public function getEnclosure()
94 | {
95 | return $this->enclosure;
96 | }
97 |
98 | /**
99 | * Set escape
100 | * @param string $escape
101 | * @return ExporterConfig
102 | */
103 | public function setEscape($escape)
104 | {
105 | $this->escape = $escape;
106 | return $this;
107 | }
108 |
109 | /**
110 | * Return escape
111 | * @return string
112 | */
113 | public function getEscape()
114 | {
115 | return $this->escape;
116 | }
117 |
118 | /**
119 | * Set newline
120 | * @param string $newline
121 | * @return ExporterConfig
122 | */
123 | public function setNewline($newline)
124 | {
125 | $this->newline = $newline;
126 | return $this;
127 | }
128 |
129 | /**
130 | * Return newline
131 | * @return string
132 | */
133 | public function getNewline()
134 | {
135 | return $this->newline;
136 | }
137 |
138 | /**
139 | * Set from-character set
140 | * @param string $fromCharset
141 | * @return ExporterConfig
142 | */
143 | public function setFromCharset($fromCharset)
144 | {
145 | $this->fromCharset = $fromCharset;
146 | return $this;
147 | }
148 |
149 | /**
150 | * Return from-character set
151 | * @return string
152 | */
153 | public function getFromCharset()
154 | {
155 | return $this->fromCharset;
156 | }
157 |
158 | /**
159 | * Set to-character set
160 | * @param string $toCharset
161 | * @return ExporterConfig
162 | */
163 | public function setToCharset($toCharset)
164 | {
165 | $this->toCharset = $toCharset;
166 | return $this;
167 | }
168 |
169 | /**
170 | * Return to-character set
171 | * @return string
172 | */
173 | public function getToCharset()
174 | {
175 | return $this->toCharset;
176 | }
177 |
178 | /**
179 | * Set file mode
180 | * @param string $fileMode
181 | * @return ExporterConfig
182 | */
183 | public function setFileMode($fileMode)
184 | {
185 | $this->fileMode = $fileMode;
186 | return $this;
187 | }
188 |
189 | /**
190 | * Return file mode
191 | * @return string
192 | */
193 | public function getFileMode()
194 | {
195 | return $this->fileMode;
196 | }
197 |
198 | /**
199 | * Set the column headers.
200 | * @param array $columnHeaders
201 | * @return ExporterConfig
202 | */
203 | public function setColumnHeaders(array $columnHeaders)
204 | {
205 | $this->columnHeaders = $columnHeaders;
206 |
207 | return $this;
208 | }
209 |
210 | /**
211 | * Get the column headers.
212 | * @return array
213 | */
214 | public function getColumnHeaders()
215 | {
216 | return $this->columnHeaders;
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Import/Tests/Standard/Join/LexerTest.php:
--------------------------------------------------------------------------------
1 | getMock('Goodby\CSV\Import\Standard\Interpreter', array('interpret'));
23 | $interpreter->expects($this->at(0))->method('interpret')->with($lines[0]);
24 | $interpreter->expects($this->at(1))->method('interpret')->with($lines[1]);
25 | $interpreter->expects($this->at(2))->method('interpret')->with($lines[2]);
26 | $interpreter->expects($this->at(3))->method('interpret')->with($lines[3]);
27 |
28 | $config = new LexerConfig();
29 | $config->setToCharset('UTF-8')->setFromCharset('SJIS-win');
30 | $lexer = new Lexer($config);
31 | $lexer->parse($shiftJisCsv, $interpreter);
32 | }
33 |
34 | public function test_mac_excel_csv()
35 | {
36 | $csv = CSVFiles::getMacExcelCsv();
37 | $lines = CSVFiles::getMacExcelLines();
38 |
39 | $interpreter = $this->getMock('Goodby\CSV\Import\Standard\Interpreter', array('interpret'));
40 | $interpreter->expects($this->at(0))->method('interpret')->with($lines[0]);
41 | $interpreter->expects($this->at(1))->method('interpret')->with($lines[1]);
42 |
43 | $config = new LexerConfig();
44 | $lexer = new Lexer($config);
45 | $lexer->parse($csv, $interpreter);
46 | }
47 |
48 | public function test_tab_separated_csv()
49 | {
50 | $csv = CSVFiles::getTabSeparatedCsv();
51 | $lines = CSVFiles::getTabSeparatedLines();
52 |
53 | $interpreter = $this->getMock('Goodby\CSV\Import\Standard\Interpreter', array('interpret'));
54 | $interpreter->expects($this->at(0))->method('interpret')->with($lines[0]);
55 | $interpreter->expects($this->at(1))->method('interpret')->with($lines[1]);
56 |
57 | $config = new LexerConfig();
58 | $config->setDelimiter("\t");
59 | $lexer = new Lexer($config);
60 | $lexer->parse($csv, $interpreter);
61 | }
62 |
63 | public function test_colon_separated_csv()
64 | {
65 | $csv = CSVFiles::getColonSeparatedCsv();
66 | $lines = CSVFiles::getColonSeparatedLines();
67 |
68 | $interpreter = $this->getMock('Goodby\CSV\Import\Standard\Interpreter', array('interpret'));
69 | $interpreter->expects($this->at(0))->method('interpret')->with($lines[0]);
70 | $interpreter->expects($this->at(1))->method('interpret')->with($lines[1]);
71 |
72 | $config = new LexerConfig();
73 | $config->setDelimiter(':');
74 | $lexer = new Lexer($config);
75 | $lexer->parse($csv, $interpreter);
76 | }
77 |
78 | public function test_utf8_csv()
79 | {
80 | $csv = CSVFiles::getUtf8Csv();
81 | $lines = CSVFiles::getUtf8Lines();
82 |
83 | $interpreter = $this->getMock('Goodby\CSV\Import\Standard\Interpreter', array('interpret'));
84 | $interpreter->expects($this->at(0))->method('interpret')->with($lines[0]);
85 | $interpreter->expects($this->at(1))->method('interpret')->with($lines[1]);
86 |
87 | $config = new LexerConfig();
88 | $lexer = new Lexer($config);
89 | $lexer->parse($csv, $interpreter);
90 | }
91 |
92 | /**
93 | * When import CSV file with data in Japanese (2 bytes character),
94 | * data imported to database with error encoding
95 | * @link https://github.com/goodby/csv/issues/5
96 | */
97 | public function test_issue_5()
98 | {
99 | $csvFilename = CSVFiles::getIssue5CSV();
100 |
101 | $csvContents = array();
102 |
103 | $config = new LexerConfig();
104 | $config
105 | ->setToCharset('UTF-8')
106 | ->setFromCharset('UTF-8');
107 | $lexer = new Lexer($config);
108 | $interpreter = new Interpreter();
109 | $interpreter->addObserver(function(array $columns) use (&$csvContents) {
110 | $csvContents[] = $columns;
111 | });
112 |
113 | $lexer->parse($csvFilename, $interpreter);
114 |
115 | $this->assertSame(array(
116 | array("ID", "NAME", "MAKER"),
117 | array("1", "スティック型クリーナ", "alice_updated@example.com"),
118 | array("2", "bob", "bob@example.com"),
119 | array("14", "スティック型クリーナ", "tho@eample.com"),
120 | array("16", "スティック型", "carot@eample.com"),
121 | ), $csvContents);
122 | }
123 |
124 | public function test_ignore_header()
125 | {
126 | $csvFilename = CSVFiles::getIssue5CSV();
127 |
128 | $config = new LexerConfig();
129 | $config
130 | ->setIgnoreHeaderLine(true)
131 | ->setToCharset('UTF-8')
132 | ->setFromCharset('UTF-8');
133 |
134 | $lexer = new Lexer($config);
135 |
136 | $interpreter = new Interpreter();
137 | $interpreter->addObserver(function(array $columns) use (&$csvContents) {
138 | $csvContents[] = $columns;
139 | });
140 |
141 | $lexer->parse($csvFilename, $interpreter);
142 | $this->assertSame(array(
143 | array("1", "スティック型クリーナ", "alice_updated@example.com"),
144 | array("2", "bob", "bob@example.com"),
145 | array("14", "スティック型クリーナ", "tho@eample.com"),
146 | array("16", "スティック型", "carot@eample.com"),
147 | ), $csvContents);
148 | }
149 |
150 | public function test_instantiation_without_config()
151 | {
152 | $lexer = new Lexer();
153 |
154 | $this->assertInstanceOf('Goodby\CSV\Import\Standard\Lexer', $lexer);
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/Goodby/CSV/Export/Tests/Standard/Join/ExporterTest.php:
--------------------------------------------------------------------------------
1 | root = vfsStream::setup('output');
26 | }
27 |
28 | public function testExport()
29 | {
30 | $config = new ExporterConfig();
31 | $exporter = new Exporter($config);
32 |
33 | $this->assertFileNotExists('vfs://output/data.csv');
34 | $exporter->export('vfs://output/data.csv', array(
35 | array('ID', 'name', 'email'),
36 | array('1', 'alice', 'alice@example.com'),
37 | array('2', 'bob', 'bob@example.com'),
38 | ));
39 |
40 | $this->assertFileExists('vfs://output/data.csv');
41 | $expectedContents = "ID,name,email\r\n";
42 | $expectedContents .= "1,alice,alice@example.com\r\n";
43 | $expectedContents .= "2,bob,bob@example.com\r\n";
44 | $this->assertSame($expectedContents, file_get_contents('vfs://output/data.csv'));
45 | }
46 |
47 | public function test_export_with_carriage_return()
48 | {
49 | $config = new ExporterConfig();
50 | $config->setNewline("\r");
51 | $exporter = new Exporter($config);
52 | $exporter->unstrict();
53 |
54 | $this->assertFileNotExists('vfs://output/data.csv');
55 | $exporter->export('vfs://output/data.csv', array(
56 | array('aaa', 'bbb', 'ccc', 'dddd'),
57 | array('123', '456', '789'),
58 | array('"aaa"', '"bbb"', '', ''),
59 | ));
60 |
61 | $this->assertFileExists('vfs://output/data.csv');
62 | $expectedContents = "aaa,bbb,ccc,dddd\r";
63 | $expectedContents .= "123,456,789\r";
64 | $expectedContents .= '"""aaa""","""bbb""",,'."\r";
65 | $this->assertSame($expectedContents, file_get_contents('vfs://output/data.csv'));
66 | }
67 |
68 | public function testUnstrict()
69 | {
70 | $config = new ExporterConfig();
71 | $exporter = new Exporter($config);
72 | $this->assertAttributeSame(true, 'strict', $exporter);
73 | $exporter->unstrict();
74 | $this->assertAttributeSame(false, 'strict', $exporter);
75 | }
76 |
77 | /**
78 | * @expectedException \Goodby\CSV\Export\Standard\Exception\StrictViolationException
79 | */
80 | public function testStrict()
81 | {
82 | $config = new ExporterConfig();
83 | $exporter = new Exporter($config);
84 |
85 | $exporter->export('vfs://output/data.csv', array(
86 | array('a', 'b', 'c'),
87 | array('a', 'b', 'c'),
88 | array('a', 'b'),
89 | ));
90 | }
91 |
92 | /**
93 | * @requires PHP 5.4
94 | */
95 | public function test_throwing_IOException_when_failed_to_write_file()
96 | {
97 | $noWritableCsv = 'vfs://output/no-writable.csv';
98 | touch($noWritableCsv);
99 | chmod($noWritableCsv, 0444);
100 |
101 | $this->assertFalse(is_writable($noWritableCsv));
102 |
103 | $config = new ExporterConfig();
104 | $exporter = new Exporter($config);
105 |
106 | $e = null;
107 |
108 | try {
109 | $exporter->export($noWritableCsv, array(
110 | array('a', 'b', 'c'),
111 | ));
112 | } catch ( IOException $e ) {
113 |
114 | }
115 |
116 | $this->assertTrue($e instanceof IOException);
117 | $this->assertContains('failed to open', $e->getMessage());
118 | }
119 |
120 | public function test_encoding()
121 | {
122 | $csv = 'vfs://output/euc.csv';
123 | $this->assertFileNotExists($csv);
124 |
125 | $config = new ExporterConfig();
126 | $config->setToCharset('EUC-JP');
127 | $config->setNewline("\n");
128 | $exporter = new Exporter($config);
129 |
130 | $exporter->export($csv, array(
131 | array('あ', 'い', 'う', 'え', 'お'),
132 | ));
133 |
134 | $this->assertFileEquals(__DIR__.'/csv_files/euc-jp.csv', $csv);
135 | }
136 |
137 | public function test_without_encoding()
138 | {
139 | $csv = 'vfs://output/utf-8.csv';
140 | $this->assertFileNotExists($csv);
141 |
142 | $config = new ExporterConfig();
143 | $config->setNewline("\n");
144 | $exporter = new Exporter($config);
145 |
146 | $exporter->export($csv, array(
147 | array('✔', '✔', '✔'),
148 | array('★', '★', '★'),
149 | ));
150 |
151 | $this->assertFileEquals(__DIR__.'/csv_files/utf-8.csv', $csv);
152 | }
153 |
154 | public function test_unseekable_wrapper_and_custom_newline_code()
155 | {
156 | $config = new ExporterConfig();
157 | $config->setNewline("\r\n");
158 | $exporter = new Exporter($config);
159 |
160 | ob_start();
161 | $exporter->export('php://output', array(
162 | array('a', 'b', 'c'),
163 | array('1', '2', '3'),
164 | ));
165 | $output = ob_get_clean();
166 |
167 | $expectedCount = "a,b,c\r\n1,2,3\r\n";
168 | $this->assertSame($expectedCount, $output);
169 | }
170 |
171 | public function test_multiple_line_columns()
172 | {
173 | $csv = 'vfs://output/multiple-lines.csv';
174 | $this->assertFileNotExists($csv);
175 |
176 | $config = new ExporterConfig();
177 | $config->setNewline("\r\n");
178 | $exporter = new Exporter($config);
179 |
180 | $exporter->export($csv, array(
181 | array("line1\r\nline2\r\nline3", "single-line"),
182 | array("line1\r\nline2\r\nline3", "single-line"),
183 | array("line1\r\nline2\r\nline3", "single-line"),
184 | ));
185 |
186 | $this->assertFileEquals(__DIR__.'/csv_files/multiple-lines.csv', $csv);
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Goodby, CSV
2 |
3 | [](https://travis-ci.org/goodby/csv)
4 |
5 | ## What is "Goodby CSV"?
6 |
7 | Goodby CSV is a highly memory efficient, flexible and extendable open-source CSV import/export library.
8 |
9 | ```php
10 | use Goodby\CSV\Import\Standard\Lexer;
11 | use Goodby\CSV\Import\Standard\Interpreter;
12 | use Goodby\CSV\Import\Standard\LexerConfig;
13 |
14 | $lexer = new Lexer(new LexerConfig());
15 | $interpreter = new Interpreter();
16 | $interpreter->addObserver(function(array $row) {
17 | // do something here.
18 | // for example, insert $row to database.
19 | });
20 | $lexer->parse('data.csv', $interpreter);
21 | ```
22 |
23 |
24 | ### Features
25 |
26 | #### 1. Memory Management Free
27 |
28 | This library was designed for low memory usage. It will not accumulate all the rows in the memory. The importer reads a CSV file and executes a callback function line by line.
29 |
30 | #### 2. Multibyte support
31 |
32 | This library supports mulitbyte input/output: for example, SJIS-win, EUC-JP and UTF-8.
33 |
34 | #### 3. Ready to Use for Enterprise Applications
35 |
36 | Goodby CSV is fully unit-tested. The library is stable and ready to be used in large projects like enterprise applications.
37 |
38 | ## Requirements
39 |
40 | * PHP 5.3.2 or later
41 | * mbstring
42 |
43 | ## Installation
44 |
45 | Install composer in your project:
46 |
47 | ```bash
48 | curl -s http://getcomposer.org/installer | php
49 | ```
50 |
51 | Create a `composer.json` file in your project root:
52 |
53 | ```json
54 | {
55 | "require": {
56 | "goodby/csv": "*"
57 | }
58 | }
59 | ```
60 |
61 | Install via composer:
62 |
63 | ```bash
64 | php composer.phar install
65 | ```
66 |
67 | ## Documentation
68 |
69 | ### Configuration
70 |
71 | Import configuration:
72 |
73 | ```php
74 | use Goodby\CSV\Import\Standard\LexerConfig;
75 |
76 | $config = new LexerConfig();
77 | $config
78 | ->setDelimiter("\t") // Customize delimiter. Default value is comma(,)
79 | ->setEnclosure("'") // Customize enclosure. Default value is double quotation(")
80 | ->setEscape("\\") // Customize escape character. Default value is backslash(\)
81 | ->setToCharset('UTF-8') // Customize target encoding. Default value is null, no converting.
82 | ->setFromCharset('SJIS-win') // Customize CSV file encoding. Default value is null.
83 | ;
84 | ```
85 |
86 | Export configuration:
87 |
88 | ```php
89 | use Goodby\CSV\Export\Standard\ExporterConfig;
90 |
91 | $config = new ExporterConfig();
92 | $config
93 | ->setDelimiter("\t") // Customize delimiter. Default value is comma(,)
94 | ->setEnclosure("'") // Customize enclosure. Default value is double quotation(")
95 | ->setEscape("\\") // Customize escape character. Default value is backslash(\)
96 | ->setToCharset('SJIS-win') // Customize file encoding. Default value is null, no converting.
97 | ->setFromCharset('UTF-8') // Customize source encoding. Default value is null.
98 | ->setFileMode(CsvFileObject::FILE_MODE_WRITE) // Customize file mode and choose either write or append. Default value is write ('w'). See fopen() php docs
99 | ;
100 | ```
101 |
102 | ### Unstrict Row Consistency Mode
103 |
104 | By default, Goodby CSV throws `StrictViolationException` when it finds a row with a different column count to other columns. In the case you want to import such a CSV, you can call `Interpreter::unstrict()` to disable row consistency check at import.
105 |
106 | rough.csv:
107 |
108 | ```csv
109 | foo,bar,baz
110 | foo,bar
111 | foo
112 | foo,bar,baz
113 | ```
114 |
115 | ```php
116 | use Goodby\CSV\Import\Standard\Interpreter;
117 | use Goodby\CSV\Import\Standard\Lexer;
118 | use Goodby\CSV\Import\Standard\LexerConfig;
119 |
120 | $interpreter = new Interpreter();
121 | $interpreter->unstrict(); // Ignore row column count consistency
122 |
123 | $lexer = new Lexer(new LexerConfig());
124 | $lexer->parse('rough.csv', $interpreter);
125 | ```
126 |
127 | ## Examples
128 |
129 | ### Import to Database via PDO
130 |
131 | user.csv:
132 |
133 | ```csv
134 | 1,alice,alice@example.com
135 | 2,bob,bob@example.com
136 | 3,carol,carol@eample.com
137 | ```
138 |
139 | ```php
140 | use Goodby\CSV\Import\Standard\Lexer;
141 | use Goodby\CSV\Import\Standard\Interpreter;
142 | use Goodby\CSV\Import\Standard\LexerConfig;
143 |
144 | $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'root');
145 | $pdo->query('CREATE TABLE IF NOT EXISTS user (id INT, `name` VARCHAR(255), email VARCHAR(255))');
146 |
147 | $config = new LexerConfig();
148 | $lexer = new Lexer($config);
149 |
150 | $interpreter = new Interpreter();
151 |
152 | $interpreter->addObserver(function(array $columns) use ($pdo) {
153 | $stmt = $pdo->prepare('INSERT INTO user (id, name, email) VALUES (?, ?, ?)');
154 | $stmt->execute($columns);
155 | });
156 |
157 | $lexer->parse('user.csv', $interpreter);
158 | ```
159 |
160 | ### Import from TSV (tab separated values) to array
161 |
162 | temperature.tsv:
163 |
164 | ```csv
165 | 9 Tokyo
166 | 27 Singapore
167 | -5 Seoul
168 | 7 Shanghai
169 | ```
170 |
171 | ```php
172 | use Goodby\CSV\Import\Standard\Lexer;
173 | use Goodby\CSV\Import\Standard\Interpreter;
174 | use Goodby\CSV\Import\Standard\LexerConfig;
175 |
176 | $temperature = array();
177 |
178 | $config = new LexerConfig();
179 | $config->setDelimiter("\t");
180 | $lexer = new Lexer($config);
181 |
182 | $interpreter = new Interpreter();
183 | $interpreter->addObserver(function(array $row) use (&$temperature) {
184 | $temperature[] = array(
185 | 'temperature' => $row[0],
186 | 'city' => $row[1],
187 | );
188 | });
189 |
190 | $lexer->parse('temperature.tsv', $interpreter);
191 |
192 | print_r($temperature);
193 | ```
194 |
195 | ### Export from array
196 |
197 | ```php
198 | use Goodby\CSV\Export\Standard\Exporter;
199 | use Goodby\CSV\Export\Standard\ExporterConfig;
200 |
201 | $config = new ExporterConfig();
202 | $exporter = new Exporter($config);
203 |
204 | $exporter->export('php://output', array(
205 | array('1', 'alice', 'alice@example.com'),
206 | array('2', 'bob', 'bob@example.com'),
207 | array('3', 'carol', 'carol@example.com'),
208 | ));
209 | ```
210 |
211 |
212 | ### Export from database via PDO
213 |
214 | ```php
215 | use Goodby\CSV\Export\Standard\Exporter;
216 | use Goodby\CSV\Export\Standard\ExporterConfig;
217 | use Goodby\CSV\Export\Standard\CsvFileObject;
218 | use Goodby\CSV\Export\Standard\Collection\PdoCollection;
219 |
220 | $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'root');
221 |
222 | $pdo->query('CREATE TABLE IF NOT EXISTS user (id INT, `name` VARCHAR(255), email VARCHAR(255))');
223 | $pdo->query("INSERT INTO user VALUES(1, 'alice', 'alice@example.com')");
224 | $pdo->query("INSERT INTO user VALUES(2, 'bob', 'bob@example.com')");
225 | $pdo->query("INSERT INTO user VALUES(3, 'carol', 'carol@example.com')");
226 |
227 | $config = new ExporterConfig();
228 | $exporter = new Exporter($config);
229 |
230 | $stmt = $pdo->prepare("SELECT * FROM user");
231 | $stmt->execute();
232 |
233 | $exporter->export('php://output', new PdoCollection($stmt));
234 | ```
235 |
236 | ### Export with CallbackCollection
237 | ```php
238 | use Goodby\CSV\Export\Standard\Exporter;
239 | use Goodby\CSV\Export\Standard\ExporterConfig;
240 |
241 | use Goodby\CSV\Export\Standard\Collection\CallbackCollection;
242 |
243 | $data = array();
244 | $data[] = array('user', 'name1');
245 | $data[] = array('user', 'name2');
246 | $data[] = array('user', 'name3');
247 |
248 | $collection = new CallbackCollection($data, function($row) {
249 | // apply custom format to the row
250 | $row[1] = $row[1] . '!';
251 |
252 | return $row;
253 | });
254 |
255 | $config = new ExporterConfig();
256 | $exporter = new Exporter($config);
257 |
258 | $exporter->export('php://stdout', $collection);
259 | ```
260 |
261 | ### Export in Symfony2 action
262 |
263 | ```php
264 | namespace AcmeBundle\ExampleBundle\Controller;
265 |
266 | use Symfony\Bundle\FrameworkBundle\Controller\Controller;
267 | use Symfony\Component\HttpFoundation\StreamedResponse;
268 |
269 | class DefaultController extends Controller
270 | {
271 | public function csvExportAction()
272 | {
273 | $conn = $this->get('database_connection');
274 |
275 | $stmt = $conn->prepare('SELECT * FROM somewhere');
276 | $stmt->execute();
277 |
278 | $response = new StreamedResponse();
279 | $response->setStatusCode(200);
280 | $response->headers->set('Content-Type', 'text/csv');
281 | $response->setCallback(function() use($stmt) {
282 | $config = new ExporterConfig();
283 | $exporter = new Exporter($config);
284 |
285 | $exporter->export('php://output', new PdoCollection($stmt->getIterator()));
286 | });
287 | $response->send();
288 |
289 | return $response;
290 | }
291 | }
292 | ```
293 |
294 | ## License
295 |
296 | Csv is open-sourced software licensed under the MIT License - see the LICENSE file for details
297 |
298 |
299 | ## Contributing
300 |
301 | We works under test driven development.
302 |
303 | Checkout master source code from github:
304 |
305 | ```bash
306 | hub clone goodby/csv
307 | ```
308 |
309 | Install components via composer:
310 |
311 | ```
312 | # If you don't have composer.phar
313 | ./scripts/bundle-devtools.sh .
314 |
315 | # If you have composer.phar
316 | composer.phar install --dev
317 | ```
318 |
319 | Run phpunit:
320 |
321 | ```bash
322 | ./vendor/bin/phpunit
323 | ```
324 |
325 | ## Acknowledgement
326 |
327 | Credits are found within composer.json file.
328 |
--------------------------------------------------------------------------------