diff --git a/README.md b/README.md index d94fc30..f492dcb 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ $stdin = new ReadableResourceStream(STDIN); $stream = new AssocDecoder($stdin); $stream->on('data', function ($data) { - // data is a parsed element from the CSV stream + // $data is a parsed element from the CSV stream // line 1: $data = array('name' => 'test', 'id' => '1'); // line 2: $data = array('name' => 'hello world', 'id' => '2'); var_dump($data); @@ -281,7 +281,15 @@ explicitly use this class instead of the underlying [`Decoder`](#decoder). In fact, it uses the [`Decoder`](#decoder) class internally. The only difference is that this class requires the first line to include the name of headers and will use this as keys for all following row data which will be emitted as -assoc arrays. +assoc arrays. After receiving the name of headers, this class will always emit +a `headers` event with a list of header names. + +```php +$stream->on('headers', function (array $headers) { + // header line: $headers = array('name', 'id'); + var_dump($headers); +}); +``` This implies that the input stream MUST start with one row of header names and MUST use the same number of columns for all records. If the input stream does diff --git a/src/AssocDecoder.php b/src/AssocDecoder.php index 00d220a..ef6f35b 100644 --- a/src/AssocDecoder.php +++ b/src/AssocDecoder.php @@ -80,6 +80,7 @@ public function handleData($data) if ($this->expected === null) { $this->headers = $data; $this->expected = \count($data); + $this->emit('headers', array($data)); } else { if (\count($data) !== $this->expected) { $this->handleError(new \UnexpectedValueException( diff --git a/tests/AssocDecoderTest.php b/tests/AssocDecoderTest.php index 91003e0..f162110 100644 --- a/tests/AssocDecoderTest.php +++ b/tests/AssocDecoderTest.php @@ -24,47 +24,52 @@ public function setUpDecoder() public function testEmitDataWithoutNewlineWillNotForward() { + $this->decoder->on('headers', $this->expectCallableNever()); $this->decoder->on('data', $this->expectCallableNever()); $this->input->emit('data', array("hello")); } - public function testEmitDataOneLineWillBeSavedAsHeaderAndWillNotForward() + public function testEmitDataOneLineWillBeSavedAsHeaderAndWillOnlyForwardHeader() { + $this->decoder->on('headers', $this->expectCallableOnceWith(array('hello', 'world'))); $this->decoder->on('data', $this->expectCallableNever()); $this->input->emit('data', array("hello,world\n")); } - public function testEmitDataTwoLinesWillForwardOneRecord() + public function testEmitDataTwoLinesWillForwardHeaderAndOneRecord() { + $this->decoder->on('headers', $this->expectCallableOnceWith(array('name', 'partner'))); $this->decoder->on('data', $this->expectCallableOnceWith(array('name' => 'alice', 'partner' => 'bob'))); $this->input->emit('data', array("name,partner\nalice,bob\n")); } - public function testEmitDataTwoLinesWithoutTrailingNewlineWillNotForwardRecord() + public function testEmitDataTwoLinesWithoutTrailingNewlineWillOnlyForwardHeaderAndNotRecord() { + $this->decoder->on('headers', $this->expectCallableOnceWith(array('name', 'partner'))); $this->decoder->on('data', $this->expectCallableNever()); $this->input->emit('data', array("name,partner\nalice,bob")); } - public function testEmitDataTwoLinesWithCustomSemicolonWillForwardOneRecord() + public function testEmitDataTwoLinesWithCustomSemicolonWillForwardHeaderAndOneRecord() { $this->decoder = new AssocDecoder($this->input, ';'); + $this->decoder->on('headers', $this->expectCallableOnceWith(array('name', 'partner'))); $this->decoder->on('data', $this->expectCallableOnceWith(array('name' => 'alice', 'partner' => 'bob'))); $this->input->emit('data', array("name;partner\nalice;bob\n")); } - public function testEmitDataTwoLinesButWrongColumnCoundWillEmitErrorAndClose() + public function testEmitDataTwoLinesButWrongColumnCountWillForwardHeaderAndWillEmitAndErrorAndClose() { + $this->decoder->on('headers', $this->expectCallableOnceWith(array('name', 'partner'))); $this->decoder->on('data', $this->expectCallableNever()); $this->decoder->on('error', $this->expectCallableOnceWith($this->isInstanceOf('UnexpectedValueException'))); $this->decoder->on('close', $this->expectCallableOnce()); - $this->input->emit('data', array("name,partner\nalice\n")); }