├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── composer.json
├── example
├── benchmarkReader
├── benchmarkWriter
├── cli
├── file
│ └── .empty
├── reader
└── writer
├── phpunit.xml.dist
├── source
├── AbstractBase.php
├── AbstractFactory.php
├── BaseInterface.php
├── FactoryInterface.php
├── Filter
│ ├── AbstractFilter.php
│ └── PermeableFilter.php
├── InvalidArgumentException.php
├── Reader
│ ├── EasyCsvReaderAdapter.php
│ ├── FilteredReader.php
│ ├── FilteredReaderFactory.php
│ ├── Reader.php
│ ├── ReaderFactory.php
│ └── ReaderInterface.php
├── RuntimeException.php
└── Writer
│ ├── EasyCsvWriterAdapter.php
│ ├── FilteredWriter.php
│ ├── FilteredWriterFactory.php
│ ├── FilteredWriterForPhp3Dot3.php
│ ├── Writer.php
│ ├── WriterFactory.php
│ ├── WriterForPhp5Dot3.php
│ └── WriterInterface.php
└── test
├── AbstractTestCase.php
├── EasyCsvReaderAdapterTest.php
├── EasyCsvWriterAdapterTest.php
├── FilteredReaderTest.php
├── FilteredWriterTest.php
├── ReaderTest.php
├── WriterTest.php
├── bootstrap.php
└── data
└── .gitignore
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | composer.lock
3 | example/file/*.csv
4 | vendor
5 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | paths:
3 | - source/*
4 | excluded_paths:
5 | - vendor/*
6 |
7 | checks:
8 | php:
9 | code_rating: true
10 | duplication: true
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.6
4 | - 7.0
5 | - 7.1
6 | - 7.2
7 | - nightly
8 | matrix:
9 | allow_failures:
10 | - php: nightly
11 | before_script:
12 | - composer self-update
13 | - composer install
14 | - phpenv rehash
15 | script:
16 | - vendor/bin/phpunit -v --colors --coverage-text
17 | notifications:
18 | email:
19 | - artodeto@bazzline.net
20 | sudo: false
21 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](http://keepachangelog.com/)
6 | and this project adheres to [Semantic Versioning](http://semver.org/).
7 |
8 | ## [Open]
9 |
10 | * add example for filter usage
11 | * add documentation for filter usage
12 | * extend unit tests
13 | * implement \_\_clone();
14 | * implement usage of filter in writer::copy
15 | * write documentation
16 | * write adapter to easy up migration from [EasyCsv - 0.0.2](https://github.com/jwage/easy-csv/tree/0.0.2/lib/EasyCSV) to this component
17 |
18 | ### To Add
19 |
20 | ### To Change
21 |
22 | ## [Unreleased]
23 |
24 | ### Added
25 |
26 | ### Changed
27 |
28 | ## [1.7.0](https://github.com/bazzline/php_component_csv/tree/1.6.0) - released at 2018-11-07
29 |
30 | ### Added
31 |
32 | * add the php-nightly test in Travis CI build and let this allow failure.
33 |
34 | ### Changed
35 |
36 | * fixed broken composer.json
37 | * let the package require PHP >=5.6 version.
38 | * remove the additional space.
39 | * we don't guarantee that the php-nightly test will always be successful.
40 | * set the different PHPUnit version to support multiple PHP versions.
41 | * using the class-based PHPUnit namespace to be compatible with the latest stable PHPUnit version.
42 |
43 | ## [1.6.0](https://github.com/bazzline/php_component_csv/tree/1.6.0) - released at 2017-05-28
44 |
45 | ### Changed
46 |
47 | * dropped support for php version below 5.6
48 | * moved release history into changelog
49 | * replaced deprecated array syntax "array()" with "[]"
50 |
51 | ## [1.5.14](https://github.com/bazzline/php_component_csv/tree/1.5.14) - released at 2016-05-30
52 |
53 | ### Changed
54 |
55 | * relaxed dependency for mockery
56 |
57 | ## [1.5.13](https://github.com/bazzline/php_component_csv/tree/1.5.13) - released at 2016-03-16
58 |
59 | ### Changed
60 |
61 | * updated dependency
62 |
63 | ## [1.5.12](https://github.com/bazzline/php_component_csv/tree/1.5.12) - released at 2016-02-22
64 |
65 | ### Changed
66 |
67 | * moved to psr-4 autoloading
68 | * removed build status from scrutinizer section
69 | * updated depencenies
70 |
71 | ## [1.5.11](https://github.com/bazzline/php_component_csv/tree/1.5.11) - released at 2016-01-20
72 |
73 | ### Changed
74 |
75 | * updated depencenies
76 |
77 | ## [1.5.10](https://github.com/bazzline/php_component_csv/tree/1.5.10) - released at 2016-01-12
78 |
79 | ### Changed
80 |
81 | * fixed dependency handling for phpunit 4.8.*
82 |
83 | ## [1.5.9](https://github.com/bazzline/php_component_csv/tree/1.5.9) - released at 2015-12-11
84 |
85 | ### Changed
86 |
87 | * updated dependencies
88 |
89 | ## [1.5.8](https://github.com/bazzline/php_component_csv/tree/1.5.8) - released at 2015-11-08
90 |
91 | ### Changed
92 |
93 | * updated dependencies
94 |
95 | ## [1.5.7](https://github.com/bazzline/php_component_csv/tree/1.5.7) - released at 2015-10-07
96 |
97 | ### Changed
98 |
99 | * updated dependencies
100 |
101 | ## [1.5.6](https://github.com/bazzline/php_component_csv/tree/1.5.6) - released at 2015-09-10
102 |
103 | ### Changed
104 |
105 | * relaxed dependencies
106 |
107 | ## [1.5.5](https://github.com/bazzline/php_component_csv/tree/1.5.5) - released at 2015-09-10
108 |
109 | ### Changed
110 |
111 | * relaxed dependencies
112 |
113 | ## [1.5.4](https://github.com/bazzline/php_component_csv/tree/1.5.4) - released at 2015-09-09
114 |
115 | ### Added
116 |
117 | * added `BaseInterface`, `ReaderInterface` and `WriterInterface`
118 |
119 | ## [1.5.3](https://github.com/bazzline/php_component_csv/tree/1.5.3) - released at 2015-08-26
120 |
121 | ### Changed
122 |
123 | * updated dependencies
124 |
125 | ## [1.5.2](https://github.com/bazzline/php_component_csv/tree/1.5.2) - released at 2015-07-06
126 |
127 | ### Changed
128 |
129 | * refactored [cli](https://github.com/bazzline/php_component_csv/blob/master/example/cli) example by using [php_component_cli_readline](https://github.com/bazzline/php_component_cli_readline)
130 |
131 | ## [1.5.1](https://github.com/bazzline/php_component_csv/tree/1.5.1) - released at 2015-07-04
132 |
133 | ### Changed
134 |
135 | * updated dependency
136 |
137 | ## [1.5.0](https://github.com/bazzline/php_component_csv/tree/1.5.0) - released at 2015-07-02
138 |
139 | ### Added
140 |
141 | * added dependency to [generic agreement](https://github.com/bazzline/php_component_generic_agreement)
142 |
143 | ### Changed
144 |
145 | * replaced own [FilterInterface](https://github.com/bazzline/php_component_csv/blob/1.4.0/source/Net/Bazzline/Component/Csv/Filter/FilterInterface.php) with external [FilterInterface](https://github.com/bazzline/php_component_generic_agreement/blob/master/source/Net/Bazzline/Component/GenericAgreement/Data/FilterableInterface.php)
146 |
147 | ## [1.4.0](https://github.com/bazzline/php_component_csv/tree/1.4.0) - released at 2015-07-02
148 |
149 | ### Added
150 |
151 | * started [cli](https://github.com/bazzline/php_component_csv/blob/master/example/cli) example to easy up usage
152 | * added "rewind" call when using reader::readAll() and reader::readMany()
153 |
154 | ## [1.3.0](https://github.com/bazzline/php_component_csv/tree/1.3.0) - released at 2015-06-26
155 |
156 | ### Added
157 |
158 |
159 | * added headline output support as keys for Reader::readMany()
160 | * added headline output support as keys for Reader::readOne()
161 | * can be disabled by Reader::disableAddHeadlineToOutput()
162 | * can be enabled by Reader::enableAddHeadlineToOutput()
163 | * is enabled by default
164 |
165 | ### Changed
166 |
167 | * fixed broken unit test for php 5.3
168 | * moved complex array combine into [own project](https://github.com/bazzline/php_component_toolbox/blob/master/source/Net/Bazzline/Component/Toolbox/HashMap/Combine.php)
169 | * removed duplicated code in Reader
170 |
171 | ## [1.2.0](https://github.com/bazzline/php_component_csv/tree/1.2.0) - released at 2015-06-25
172 |
173 | ### Added
174 |
175 | * added examples ([benchmarkReader](https://github.com/bazzline/php_component_csv/blob/master/example/benchmarkReader), [benchmarkWriter](https://github.com/bazzline/php_component_csv/blob/master/example/benchmarkWriter), [reader](https://github.com/bazzline/php_component_csv/blob/master/example/reader) and [writer](https://github.com/bazzline/php_component_csv/blob/master/example/writer))
176 | * implemented filter for reader and writer by creating the [FilterInterface](https://github.com/bazzline/php_component_csv/blob/1.2.0/source/Net/Bazzline/Component/Csv/Filter/FilterInterface.php)
177 |
178 | ## [1.1.0](https://github.com/bazzline/php_component_csv/tree/1.1.0) - released at 2015-06-10
179 |
180 | ### Added
181 |
182 | * added link to api
183 | * added minimum php version requirement
184 | * implemented "move($path)" method into [Writer](https://github.com/bazzline/php_component_csv/blob/master/source/Net/Bazzline/Component/Csv/Writer/Writer.php)
185 |
186 | ### Changed
187 |
188 | * removed "TODO"
189 | * updated dependencies
190 |
191 | ## [1.0.0](https://github.com/bazzline/php_component_csv/tree/1.0.0) - released at 2015-06-07
192 |
193 | ### Added
194 |
195 | * initial release
196 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**.
4 |
5 | We accept contributions via Pull Requests on [Github](https://github.com/spatie/laravel-backup).
6 |
7 |
8 | ## Pull Requests
9 |
10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
11 |
12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests.
13 |
14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
15 |
16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
17 |
18 | - **Create feature branches** - Don't ask us to pull from your master branch.
19 |
20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
21 |
22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
23 |
24 |
25 | ## Running Tests
26 |
27 | ``` bash
28 | $ vendor/bin/phpunit -v --colors --coverage-text
29 | ```
30 |
31 |
32 | **Happy coding**!
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CSV Handling Component for PHP
2 |
3 | This project aims to deliver an easy to use and free as in freedom php compontent for dealing with csv files (read and write).
4 |
5 | This component is heavily influenced by [jwage/easy-csv](https://github.com/jwage/easy-csv).
6 | It was mainly created because of missing compatibility with php 5.6 and no official packagist support from [jwage/easy-csv](https://github.com/jwage/easy-csv).
7 |
8 | The build status of the current master branch is tracked by Travis CI:
9 | [](http://travis-ci.org/bazzline/php_component_csv)
10 | [](https://packagist.org/packages/net_bazzline/php_component_csv)
11 |
12 | The scrutinizer status are:
13 | [](https://scrutinizer-ci.com/g/bazzline/php_component_csv/)
14 |
15 | The versioneye status is:
16 | [](https://www.versioneye.com/user/projects/557492b1316137000d0000d0)
17 |
18 | Take a look on [openhub.net](https://www.openhub.net/p/php_component_csv).
19 |
20 | The current change log can be found [here](https://github.com/bazzline/php_component_csv/blob/master/CHANGELOG.md).
21 |
22 | # Benefits
23 |
24 | * low and stable memory usage (give it a try by using [benchmarkReader](https://github.com/bazzline/php_component_csv/blob/master/example/benchmarkReader) and [benchmarkWriter](https://github.com/bazzline/php_component_csv/blob/master/example/benchmarkWriter))
25 | * works with PHP 5.6 and above
26 | * \_\_invoke() implemented to use it as function
27 | * unified reader and writer
28 | * adapter to easy up migration from [EasyCsv - 0.0.1](https://github.com/jwage/easy-csv/tree/0.0.1/lib/EasyCSV) to this component
29 | * [writer](https://github.com/jwage/easy-csv/blob/master/lib/EasyCSV/Writer.php)
30 | * [reader](https://github.com/jwage/easy-csv/blob/master/lib/EasyCSV/Reader.php)
31 | * usage of [filters](https://github.com/bazzline/php_component_csv/blob/master/source/Net/Bazzline/Component/Csv/Filter) - control what comes in and what comes out
32 | * reader
33 | * implemented iterator
34 | * readOne();
35 | * readMany();
36 | * readAll();
37 | * writer
38 | * copy();
39 | * delete();
40 | * move();
41 | * truncate();
42 | * writeOne();
43 | * writeMany();
44 | * writeAll(); //truncates file and writes content
45 |
46 | # Install
47 |
48 | ## By Hand
49 |
50 | ```
51 | mkdir -p vendor/net_bazzline/php_component_csv
52 | cd vendor/net_bazzline/php_component_csv
53 | git clone https://github.com/bazzline/php_component_csv .
54 | ```
55 |
56 | ## With [Packagist](https://packagist.org/packages/net_bazzline/php_component_csv)
57 |
58 | ```
59 | composer require net_bazzline/php_component_csv:dev-master
60 | ```
61 |
62 | # Usage
63 |
64 | ## [Reader](http://www.bazzline.net/55371e9f93dbdec83dc82730a5a73db5fc36272e/class-Net.Bazzline.Component.Csv.Reader.Reader.html)
65 |
66 | ### Read Content
67 |
68 | ```php
69 | $reader = new Reader('my/file.csv');
70 | //I am using json_encode() since there is no official and best way how to
71 | // output arrays on the command line.
72 |
73 | //read one line
74 | echo json_encode($reader->readOne()) . PHP_EOL;
75 |
76 | //read 10 lines
77 | foreach ($reader->readMany(10) as $line) {
78 | echo json_encode($line) . PHP_EOL;
79 | }
80 |
81 | //read all lines
82 | foreach ($reader->readAll() as $line) {
83 | echo json_encode($line) . PHP_EOL;
84 | }
85 | ```
86 |
87 | #### By Iteration
88 |
89 | ```php
90 | $reader = new Reader('my/file.csv');
91 | //I am using json_encode() since there is no official and best way how to
92 | // output arrays on the command line.
93 |
94 | if ($reader->hasHeadline()) {
95 | echo 'headlines: ' . json_encode($reader->readHeadline());
96 | }
97 |
98 | foreach ($reader as $line) {
99 | echo json_encode($line) . PHP_EOL;
100 | }
101 | ```
102 |
103 | #### By Using As A Function
104 |
105 | ```php
106 | $reader = new Reader('my/file.csv');
107 | //I am using json_encode() since there is no official and best way how to
108 | // output arrays on the command line.
109 |
110 | while ($line = $reader()) {
111 | echo json_encode($line) . PHP_EOL;
112 | }
113 | ```
114 |
115 | ## [Writer](http://www.bazzline.net/55371e9f93dbdec83dc82730a5a73db5fc36272e/class-Net.Bazzline.Component.Csv.Writer.Writer.html)
116 |
117 | ### Write Content
118 |
119 | #### By Iteration
120 |
121 | ```php
122 | //$headlines contains a php array
123 | //$lines contains a php array of arrays
124 | $writer = new Writer('my/file.csv');
125 |
126 | $writer->writeHeadline($headlines);
127 |
128 | foreach ($lines as $line) {
129 | $writer->writeOne($line);
130 | }
131 | ```
132 |
133 | #### At Once
134 |
135 | ```php
136 | //$headlines contains a php array
137 | //$lines contains a php array of arrays
138 | $writer = new Writer('my/file.csv');
139 |
140 | $writer->writeHeadline($headlines);
141 | $writer->writeMany($lines);
142 | ```
143 |
144 | #### By Using As A Function
145 |
146 | ```php
147 | //$line contains a php array
148 | //$lines contains a php array of arrays
149 | $writer = new Writer('my/file.csv');
150 |
151 | $writer($line);
152 |
153 | foreach ($lines as $line) {
154 | $writer($line);
155 | }
156 | ```
157 |
158 | ### Truncate
159 |
160 | ```php
161 | $writer = new Writer('my/file.csv');
162 |
163 | $writer->truncate();
164 | ```
165 |
166 | ### Copy
167 |
168 | ```php
169 | $writer = new Writer('my/file.csv');
170 |
171 | $writer->copy('my/my_first_copy.csv'); //writer will still write into "file.csv"
172 |
173 | $writer->copy('my/my_second_copy.csv', true); //writer will write in "my_second_copy.csv"
174 | ```
175 |
176 | ### Move
177 |
178 | ```php
179 | $writer = new Writer('my/file.csv');
180 |
181 | $writer->move('my/new_name.csv'); //writer will write in "new_name.csv"
182 | ```
183 |
184 | # API
185 |
186 | [API](http://www.bazzline.net/55371e9f93dbdec83dc82730a5a73db5fc36272e/index.html) is available at [bazzline.net](http://www.bazzline.net).
187 |
188 | # Other Great Components
189 |
190 | * [goodby/csv](https://github.com/goodby/csv)
191 | * [thephpleague/csv](https://github.com/thephpleague/csv)
192 | * [keboola/php-csv](https://github.com/keboola/php-csv)
193 | * [ajgarlag/AiglCsv](https://github.com/ajgarlag/AjglCsv)
194 | * [jwage/easy-csv](https://github.com/jwage/easy-csv)
195 | * [swt83/php-csv](https://github.com/swt83/php-csv)
196 |
197 | # Hall of Fame - The list of contributors
198 |
199 | * [peter279k](https://github.com/peter279k) - [homepage](https://peterli.website)
200 | * [stevleibelt](https://github.com/stevleibelt) - [homepage](https://stev.leibelt.de)
201 |
202 | # Contributing
203 |
204 | Please see [CONTRIBUTING](https://github.com/bazzline/php_component_csv/blob/master/CONTRIBUTING.md) for details.
205 |
206 | # Final Words
207 |
208 | Star it if you like it :-). Add issues if you need it. Pull patches if you enjoy it. Write a blog entry if you use it. [Donate something](https://gratipay.com/~stevleibelt) if you love it :-].
209 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "net_bazzline/php_component_csv",
3 | "description": "free as in freedom php component to easy up usage (reading and writing) of csv files for php 5.6 and above",
4 | "keywords": [
5 | "bazzline",
6 | "psr",
7 | "psr-4",
8 | "php",
9 | "php56",
10 | "php7",
11 | "component",
12 | "filesystem",
13 | "easy",
14 | "csv",
15 | "easycsv",
16 | "adapter",
17 | "read",
18 | "write",
19 | "lgpl",
20 | "filter",
21 | "import",
22 | "export",
23 | "headline",
24 | "invoke",
25 | "tranversable",
26 | "iterator",
27 | "reusable",
28 | "free as in freedom",
29 | "low",
30 | "memory",
31 | "usage"
32 | ],
33 | "type": "library",
34 | "require": {
35 | "net_bazzline/php_component_cli_readline": "^1.2",
36 | "net_bazzline/php_component_generic_agreement": "^1.0",
37 | "net_bazzline/php_component_toolbox": "^1.3",
38 | "php": ">=5.6"
39 | },
40 | "require-dev": {
41 | "mikey179/vfsstream": "^1.6",
42 | "mockery/mockery": "^0.9",
43 | "phpunit/phpunit": "^5.7 || ^6.5"
44 | },
45 | "license": "LGPL-3.0",
46 | "authors": [
47 | {
48 | "name": "Stev Leibelt",
49 | "email": "artodeto@bazzline.net",
50 | "homepage": "https://artodeto.bazzline.net",
51 | "role": "Developer"
52 | }
53 | ],
54 | "autoload": {
55 | "psr-4": {
56 | "Net\\Bazzline\\Component\\Csv\\": "source/"
57 | }
58 | },
59 | "autoload-dev": {
60 | "psr-4": {
61 | "Test\\Net\\Bazzline\\Component\\Csv\\": "test/"
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/example/benchmarkReader:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
5 | * @since 2015-06-24
6 | */
7 |
8 | require_once __DIR__ . '/../vendor/autoload.php';
9 |
10 | $factory = new \Net\Bazzline\Component\Csv\Reader\ReaderFactory();
11 | $reader = $factory->create();
12 |
13 | try {
14 | $path = ($argc > 1) ? $argv[1] : __DIR__ . '/file/benchmark.csv';
15 |
16 | if (!file_exists($path)) {
17 | throw new Exception('invalid file path provided: "' . $path . '"');
18 | }
19 |
20 | $reader->setPath($path);
21 |
22 | $lineIterator = 0;
23 | $memoryUsageBeforeInMegabytes = (memory_get_usage(true) / 1048576);
24 | $memoryPeakUsageBeforeInMegabytes = (memory_get_peak_usage(true) / 1048576);
25 | $timeBeforeInSeconds = microtime(true);
26 |
27 | while ($line = $reader()) {
28 | ++$lineIterator;
29 | if (($lineIterator % 100) === 0) {
30 | echo '.';
31 | }
32 | }
33 | echo PHP_EOL;
34 |
35 | $memoryUsageAfterInMegabytes = (memory_get_usage(true) / 1048576);
36 | $memoryPeakUsageAfterInMegabytes = (memory_get_peak_usage(true) / 1048576);
37 | $timeAfterInMicroSeconds = microtime(true);
38 |
39 | echo 'file path: ' . realpath($path) . PHP_EOL;
40 | echo 'number of lines read: ' . $lineIterator . PHP_EOL;
41 | echo 'runtime: ' . ceil($timeAfterInMicroSeconds - $timeBeforeInSeconds) . ' seconds' . PHP_EOL;
42 | echo 'memory usage' . PHP_EOL;
43 | echo ' before writing: ' . $memoryUsageBeforeInMegabytes . ' MB' . PHP_EOL;
44 | echo ' after writing: ' . $memoryUsageAfterInMegabytes . ' MB' . PHP_EOL;
45 | echo 'memory peak usage' . PHP_EOL;
46 | echo ' before writing: ' . $memoryPeakUsageBeforeInMegabytes . ' MB' . PHP_EOL;
47 | echo ' after writing: ' . $memoryPeakUsageAfterInMegabytes . ' MB' . PHP_EOL;
48 | } catch (Exception $exception) {
49 | echo 'usage: ' . basename(__FILE__) . ' []' . PHP_EOL;
50 | echo '----------------' . PHP_EOL;
51 | echo $exception->getMessage() . PHP_EOL;
52 | return 1;
53 | }
54 |
--------------------------------------------------------------------------------
/example/benchmarkWriter:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
5 | * @since 2015-06-24
6 | */
7 |
8 | require_once __DIR__ . '/../vendor/autoload.php';
9 |
10 | $factory = new \Net\Bazzline\Component\Csv\Writer\WriterFactory();
11 | $writer = $factory->create();
12 |
13 | try {
14 | $usage = 'usage: ' . basename(__FILE__) . ' []';
15 |
16 | if ($argc < 3) {
17 | throw new Exception('invalid number of parameters provided');
18 | }
19 | array_shift($argv);
20 | end($argv);
21 |
22 | $path = (is_file(current($argv))) ? array_pop($argv) : __DIR__ . '/file/benchmark.csv';
23 | reset($argv);
24 |
25 | $writer->setPath($path);
26 |
27 | $arrayOfLine = [];
28 | $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ';
29 | $numberOfCharacters = strlen($characters);
30 | $numberOfCharacterIndex = $numberOfCharacters - 1;
31 | $numberOfColumnsPerLine = array_shift($argv);
32 | $numberOfLines = array_shift($argv);
33 |
34 | $memoryUsageBeforeInMegabytes = (memory_get_usage(true) / 1048576);
35 | $memoryPeakUsageBeforeInMegabytes = (memory_get_peak_usage(true) / 1048576);
36 | $timeBeforeInMicroSeconds = microtime(true);
37 |
38 | for ($lineIterator = 0; $lineIterator < $numberOfLines; ++$lineIterator) {
39 | $columns = [];
40 |
41 | for ($columnIterator = 0; $columnIterator < $numberOfColumnsPerLine; ++$columnIterator) {
42 | $column = '';
43 | $numberOfCharactersPerColumn = mt_rand(1, $numberOfCharacters);
44 |
45 | for ($characterIterator = 0; $characterIterator < $numberOfCharactersPerColumn; ++$characterIterator) {
46 | $index = mt_rand(0, $numberOfCharacterIndex);
47 | $column .= $characters[$index];
48 | }
49 |
50 | $columns[] = $column;
51 | }
52 |
53 | $line = implode(',', $columns);
54 |
55 | if ($writer($line) === false) {
56 | throw new Exception('could not write line "' . $line . '" to file "' . $path . '"');
57 | } else {
58 | if (($lineIterator % 100) === 0) {
59 | echo '.';
60 | }
61 | }
62 | }
63 | echo PHP_EOL;
64 |
65 | $memoryUsageAfterInMegabytes = (memory_get_usage(true) / 1048576);
66 | $memoryPeakUsageAfterInMegabytes = (memory_get_peak_usage(true) / 1048576);
67 | $timeAfterInMicroSeconds = microtime(true);
68 |
69 | echo 'file path: ' . realpath($path) . PHP_EOL;
70 | echo 'number of lines written: ' . $lineIterator . PHP_EOL;
71 | echo 'runtime: ' . ceil($timeAfterInMicroSeconds - $timeBeforeInMicroSeconds) . ' seconds' . PHP_EOL;
72 | echo 'memory usage' . PHP_EOL;
73 | echo ' before writing: ' . $memoryUsageBeforeInMegabytes . ' MB' . PHP_EOL;
74 | echo ' after writing: ' . $memoryUsageAfterInMegabytes . ' MB' . PHP_EOL;
75 | echo 'memory peak usage' . PHP_EOL;
76 | echo ' before writing: ' . $memoryPeakUsageBeforeInMegabytes . ' MB' . PHP_EOL;
77 | echo ' after writing: ' . $memoryPeakUsageAfterInMegabytes . ' MB' . PHP_EOL;
78 | } catch (Exception $exception) {
79 | echo $usage . PHP_EOL;
80 | echo '----------------' . PHP_EOL;
81 | echo $exception->getMessage() . PHP_EOL;
82 | return 1;
83 | }
84 |
--------------------------------------------------------------------------------
/example/cli:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
5 | * @since 2015-06-30
6 | * @see:
7 | * https://github.com/stevleibelt/examples/blob/master/php/cli/readline.php
8 | * https://github.com/yiisoft/yii2/issues/7974
9 | * https://github.com/ErikDubbelboer/php-repl/blob/master/repl.php
10 | */
11 |
12 | use Net\Bazzline\Component\Cli\Readline\ManagerFactory;
13 | use Net\Bazzline\Component\Csv\Reader\ReaderFactory;
14 | use Net\Bazzline\Component\Csv\Writer\WriterFactory;
15 |
16 | require_once __DIR__ . '/../vendor/autoload.php';
17 |
18 | try {
19 | $path = ($argc > 1) ? $argv[1] : __DIR__ . '/file/example.csv';
20 |
21 | if (!file_exists($path)) {
22 | throw new Exception('invalid file path provided: "' . $path . '"');
23 | }
24 |
25 | $managerFactory = new ManagerFactory();
26 | $readerFactory = new ReaderFactory();
27 | $writerFactory = new WriterFactory();
28 |
29 | $manager = $managerFactory->create();
30 | $reader = $readerFactory->create();
31 | $writer = $writerFactory->create();
32 |
33 | $reader->setPath($path);
34 | $writer->setPath($path);
35 |
36 | $manager->setConfiguration(
37 | [
38 | 'exit' => function () {
39 | exit(0);
40 | },
41 | 'help' => function () {
42 | echo 'usage: ' . PHP_EOL .
43 | ' ' . basename(__FILE__) . ' [path to csv file]' . PHP_EOL;
44 | },
45 | 'read' => [
46 | 'all' => function () use ($reader) {
47 | foreach ($reader->readAll() as $line) {
48 | echo implode("\t", $line) . PHP_EOL;
49 | }
50 | },
51 | 'many' => function($length = null, $start = null) use ($reader) {
52 | $showUsage = (is_null($length) || ((int) $length === 0));
53 | if ($showUsage) {
54 | echo 'usage: ' . PHP_EOL .
55 | ' many []' . PHP_EOL;
56 | } else {
57 | foreach ($reader->readMany($length, $start) as $line) {
58 | echo implode("\t", $line) . PHP_EOL;
59 | }
60 | }
61 | },
62 | 'one' => function ($lineNumber = null) use ($reader) {
63 | $showUsage = (is_null($lineNumber));
64 | if ($showUsage) {
65 | echo 'usage: ' . PHP_EOL .
66 | ' one ' . PHP_EOL;
67 | } else {
68 | $line = $reader->readOne($lineNumber);
69 |
70 | if (is_array($line)) {
71 | echo implode("\t", $line) . PHP_EOL;
72 | } else if (is_scalar($line)) {
73 | echo $line . PHP_EOL;
74 | }
75 | }
76 | }
77 | ],
78 | 'write' => [
79 | 'all' => function ($lines = null) use ($writer) {
80 | //@todo move into closure to reuse it
81 | $lines = func_get_args();
82 | $numberOfLines = false;
83 |
84 | if (is_array($lines)) {
85 | $numberOfLines = $writer->writeMany($lines);
86 | }
87 |
88 | if ($numberOfLines === false) {
89 | echo 'no lines where written' . PHP_EOL;
90 | } else {
91 | echo count($lines) . ' lines written' . PHP_EOL;
92 | }
93 | },
94 | 'many' => function () use ($writer) {
95 | $lines = func_get_args();
96 | $numberOfLines = false;
97 |
98 | if (is_array($lines)) {
99 | $numberOfLines = $writer->writeMany($lines);
100 | }
101 |
102 | if ($numberOfLines === false) {
103 | echo 'no lines where written' . PHP_EOL;
104 | } else {
105 | echo count($lines) . ' lines written' . PHP_EOL;
106 | }
107 | },
108 | 'one' => function () use ($writer) {
109 | $arguments = func_get_args();
110 | $numberOfArguments = count($arguments);
111 |
112 | if ($numberOfArguments === 1) {
113 | $line = $arguments[0];
114 | $numberOfLines = (is_scalar($line)) ? $writer->writeOne($line) : false;
115 | } else if ($numberOfArguments > 1) {
116 | $line = implode(',', $arguments);
117 | $numberOfLines = (is_scalar($line)) ? $writer->writeOne($line) : false;
118 | } else {
119 | $numberOfLines = false;
120 | }
121 |
122 | if ($numberOfLines === false) {
123 | echo 'no lines where written' . PHP_EOL;
124 | } else {
125 | echo '1 line written' . PHP_EOL;
126 | }
127 | }
128 | ]
129 | ]
130 | );
131 | $manager->setPrompt('csv cli: ');
132 | $manager->run();
133 | } catch (Exception $exception) {
134 | echo 'usage: ' . basename(__FILE__) . ' []' . PHP_EOL;
135 | echo '----------------' . PHP_EOL;
136 | echo $exception->getMessage() . PHP_EOL;
137 | return 1;
138 | }
139 |
--------------------------------------------------------------------------------
/example/file/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bazzline/php_component_csv/539360c4aed599d52756f8ddb1e3fb206742b3ca/example/file/.empty
--------------------------------------------------------------------------------
/example/reader:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
5 | * @since 2015-06-24
6 | */
7 |
8 | require_once __DIR__ . '/../vendor/autoload.php';
9 |
10 | $factory = new \Net\Bazzline\Component\Csv\Reader\ReaderFactory();
11 | $reader = $factory->create();
12 |
13 | try {
14 | $path = ($argc > 1) ? $argv[1] : __DIR__ . '/file/example.csv';
15 |
16 | if (!file_exists($path)) {
17 | throw new Exception('invalid file path provided: "' . $path . '"');
18 | }
19 |
20 | $reader->setPath($path);
21 |
22 | while ($line = $reader()) {
23 | echo implode("\t", $line) . PHP_EOL;
24 | }
25 | } catch (Exception $exception) {
26 | echo 'usage: ' . basename(__FILE__) . ' []' . PHP_EOL;
27 | echo '----------------' . PHP_EOL;
28 | echo $exception->getMessage() . PHP_EOL;
29 | return 1;
30 | }
31 |
--------------------------------------------------------------------------------
/example/writer:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
5 | * @since 2015-06-24
6 | */
7 |
8 | require_once __DIR__ . '/../vendor/autoload.php';
9 |
10 | $factory = new \Net\Bazzline\Component\Csv\Writer\WriterFactory();
11 | $writer = $factory->create();
12 |
13 | try {
14 | $usage = 'usage: ' . basename(__FILE__) . ' "content,of,line,one" ["content,of,line,two"[...[]]]';
15 |
16 | if ($argc < 2) {
17 | throw new Exception('you have to provide at least one line of content');
18 | }
19 | array_shift($argv);
20 | end($argv);
21 |
22 | $path = (is_file(current($argv))) ? array_pop($argv) : __DIR__ . '/file/example.csv';
23 | reset($argv);
24 |
25 | $writer->setPath($path);
26 |
27 | $writer('asdasds" asdasd');
28 |
29 | foreach ($argv as $line) {
30 | if ($writer($line) === false) {
31 | throw new Exception('could not write line "' . $line . '" to file "' . $path . '"');
32 | }
33 | }
34 | } catch (Exception $exception) {
35 | echo $usage . PHP_EOL;
36 | echo '----------------' . PHP_EOL;
37 | echo $exception->getMessage() . PHP_EOL;
38 | return 1;
39 | }
40 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 | test/
15 |
16 |
17 |
18 |
19 | source
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/source/AbstractBase.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-06
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv;
8 |
9 | use SplFileObject;
10 |
11 | abstract class AbstractBase implements BaseInterface
12 | {
13 | /** @var string */
14 | private $delimiter = ',';
15 |
16 | /** @var string */
17 | private $enclosure = '"';
18 |
19 | /** @var string */
20 | private $escapeCharacter = '\\';
21 |
22 | /** @var false|array */
23 | private $headline = false;
24 |
25 | /** @var SplFileObject */
26 | private $handler;
27 |
28 | /** @var string */
29 | private $path;
30 |
31 | /**
32 | * @return bool
33 | */
34 | public function hasHeadline()
35 | {
36 | return ($this->headline !== false);
37 | }
38 |
39 | /**
40 | * @param string $delimiter
41 | * @throws InvalidArgumentException
42 | */
43 | public function setDelimiter($delimiter)
44 | {
45 | $this->assertIsASingleCharacterString($delimiter, 'delimiter');
46 | $this->delimiter = $delimiter;
47 | $this->updateCsvControl();
48 | }
49 |
50 | /**
51 | * @param string $enclosure
52 | * @throws InvalidArgumentException
53 | */
54 | public function setEnclosure($enclosure)
55 | {
56 | $this->assertIsASingleCharacterString($enclosure, 'enclosure');
57 | $this->enclosure = $enclosure;
58 | $this->updateCsvControl();
59 | }
60 |
61 | /**
62 | * @param string $escapeCharacter
63 | * @throws InvalidArgumentException
64 | */
65 | public function setEscapeCharacter($escapeCharacter)
66 | {
67 | $this->assertIsASingleCharacterString($escapeCharacter, 'escapeCharacter');
68 | $this->escapeCharacter = $escapeCharacter;
69 | $this->updateCsvControl();
70 | }
71 |
72 | /**
73 | * @param string $path
74 | * @return $this
75 | * @throws InvalidArgumentException
76 | * @todo implement validation
77 | */
78 | public function setPath($path)
79 | {
80 | $this->path = $path;
81 | $this->handler = $this->open($path);
82 |
83 | return $this;
84 | }
85 |
86 | /**
87 | * @return string
88 | */
89 | protected function getDelimiter()
90 | {
91 | return $this->delimiter;
92 | }
93 |
94 | /**
95 | * @return string
96 | */
97 | protected function getEnclosure()
98 | {
99 | return $this->enclosure;
100 | }
101 |
102 | /**
103 | * @return string
104 | */
105 | protected function getEscapeCharacter()
106 | {
107 | return $this->escapeCharacter;
108 | }
109 |
110 | /**
111 | * @return SplFileObject|resource
112 | */
113 | protected function getFileHandler()
114 | {
115 | return $this->handler;
116 | }
117 |
118 | /**
119 | * @return string
120 | */
121 | abstract protected function getFileHandlerOpenMode();
122 |
123 | /**
124 | * @return array|false
125 | */
126 | protected function getHeadline()
127 | {
128 | return $this->headline;
129 | }
130 |
131 | /**
132 | * @return string
133 | */
134 | protected function getPath()
135 | {
136 | return $this->path;
137 | }
138 |
139 | /**
140 | * @return $this
141 | */
142 | protected function resetHeadline()
143 | {
144 | $this->headline = false;
145 |
146 | return $this;
147 | }
148 |
149 | /**
150 | * @param array $headline
151 | * @return $this
152 | */
153 | protected function setHeadline(array $headline)
154 | {
155 | $this->headline = $headline;
156 |
157 | return $this;
158 | }
159 |
160 | protected function close()
161 | {
162 | if (!is_null($this->handler)) {
163 | $this->headline = null;
164 | }
165 | }
166 |
167 | /**
168 | * @param string $path
169 | * @return SplFileObject
170 | * @todo inject or inject factory
171 | */
172 | protected function open($path)
173 | {
174 | $file = new SplFileObject($path, $this->getFileHandlerOpenMode());
175 | $file->setFlags(SplFileObject::READ_CSV | SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY);
176 |
177 | return $file;
178 | }
179 |
180 | /**
181 | * @param string $variable
182 | * @param string $name
183 | * @throws InvalidArgumentException
184 | */
185 | private function assertIsASingleCharacterString($variable, $name)
186 | {
187 | if (!is_string($variable)) {
188 | $message = $name . ' must be of type "string"';
189 |
190 | throw new InvalidArgumentException($message);
191 | }
192 | if (strlen($variable) != 1) {
193 | $message = $name . ' must be a single character';
194 |
195 | throw new InvalidArgumentException($message);
196 | }
197 | }
198 |
199 | private function updateCsvControl()
200 | {
201 | $file = $this->getFileHandler();
202 |
203 | if ($file instanceof SplFileObject) {
204 | $file->setCsvControl(
205 | $this->getDelimiter(),
206 | $this->getEnclosure(),
207 | $this->getEscapeCharacter()
208 | );
209 | }
210 | }
211 | }
--------------------------------------------------------------------------------
/source/AbstractFactory.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-06
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv;
8 |
9 | /**
10 | * Class AbstractFactory
11 | * @package Net\Bazzline\Component\Csv
12 | */
13 | abstract class AbstractFactory implements FactoryInterface
14 | {
15 | /**
16 | * @return string
17 | */
18 | protected function getDelimiter()
19 | {
20 | return ',';
21 | }
22 |
23 | /**
24 | * @return string
25 | */
26 | protected function getEnclosure()
27 | {
28 | return '"';
29 | }
30 |
31 | /**
32 | * @return string
33 | */
34 | protected function getEscapeCharacter()
35 | {
36 | return "\\";
37 | }
38 | }
--------------------------------------------------------------------------------
/source/BaseInterface.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-09-09
5 | */
6 | namespace Net\Bazzline\Component\Csv;
7 |
8 | /**
9 | * Interface BaseInterface
10 | * @package Net\Bazzline\Component\Csv
11 | */
12 | interface BaseInterface
13 | {
14 | /**
15 | * @return bool
16 | */
17 | public function hasHeadline();
18 |
19 | /**
20 | * @param string $delimiter
21 | * @throws InvalidArgumentException
22 | */
23 | public function setDelimiter($delimiter);
24 |
25 | /**
26 | * @param string $enclosure
27 | * @throws InvalidArgumentException
28 | */
29 | public function setEnclosure($enclosure);
30 |
31 | /**
32 | * @param string $escapeCharacter
33 | * @throws InvalidArgumentException
34 | */
35 | public function setEscapeCharacter($escapeCharacter);
36 |
37 | /**
38 | * @param string $path
39 | * @return $this
40 | * @throws InvalidArgumentException
41 | */
42 | public function setPath($path);
43 | }
--------------------------------------------------------------------------------
/source/FactoryInterface.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-06
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv;
8 |
9 | interface FactoryInterface
10 | {
11 | /**
12 | * @return object
13 | */
14 | public function create();
15 | }
--------------------------------------------------------------------------------
/source/Filter/AbstractFilter.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-07-02
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Filter;
8 |
9 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
10 | use Net\Bazzline\Component\GenericAgreement\Exception\ExceptionInterface;
11 |
12 | abstract class AbstractFilter implements FilterableInterface
13 | {
14 | /**
15 | * @param mixed $data
16 | * @return null|mixed
17 | * @throws ExceptionInterface
18 | */
19 | public function __invoke($data)
20 | {
21 | return $this->filter($data);
22 | }
23 | }
--------------------------------------------------------------------------------
/source/Filter/PermeableFilter.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-14
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Filter;
8 |
9 | use Net\Bazzline\Component\GenericAgreement\Exception\ExceptionInterface;
10 |
11 | class PermeableFilter extends AbstractFilter
12 | {
13 | /**
14 | * @param mixed $data
15 | * @return null|mixed
16 | * @throws ExceptionInterface
17 | */
18 | public function filter($data)
19 | {
20 | return $data;
21 | }
22 | }
--------------------------------------------------------------------------------
/source/InvalidArgumentException.php:
--------------------------------------------------------------------------------
1 |
4 | * @since: 2015-04-24
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv;
8 |
9 | use InvalidArgumentException as ParentClass;
10 |
11 | class InvalidArgumentException extends ParentClass {}
12 |
--------------------------------------------------------------------------------
/source/Reader/EasyCsvReaderAdapter.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-16
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Reader;
8 |
9 | /**
10 | * Class EasyCsvReaderAdapter
11 | * @package Net\Bazzline\Component\Csv\Reader
12 | */
13 | class EasyCsvReaderAdapter
14 | {
15 | /** @var ReaderInterface */
16 | private $reader;
17 |
18 | /**
19 | * @param string $path
20 | * @param string $mode - not in use
21 | * @param bool $headersInFirstRow
22 | * @param ReaderInterface $reader - optional
23 | */
24 | public function __construct($path, $mode = 'r+', $headersInFirstRow = true, ReaderInterface $reader = null)
25 | {
26 | if (is_null($reader)) {
27 | $factory = new ReaderFactory();
28 | $this->reader = $factory->create();
29 | } else {
30 | $this->reader = $reader;
31 | }
32 |
33 | $this->reader->setDelimiter(',');
34 | $this->reader->setEnclosure('"');
35 | $this->reader->setPath($path);
36 |
37 | if ($headersInFirstRow) {
38 | $this->reader->enableHasHeadline();
39 | } else {
40 | $this->reader->disableHasHeadline();
41 | }
42 | }
43 |
44 | /**
45 | * @param string $delimiter
46 | */
47 | public function setDelimiter($delimiter)
48 | {
49 | $this->reader->setDelimiter($delimiter);
50 | }
51 |
52 | /**
53 | * @param string $enclosure
54 | */
55 | public function setEnclosure($enclosure)
56 | {
57 | $this->reader->setEnclosure($enclosure);
58 | }
59 |
60 | /**
61 | * @return array|false
62 | */
63 | public function getHeaders()
64 | {
65 | $headline = $this->reader->readHeadline();
66 |
67 | return $headline;
68 | }
69 |
70 | /**
71 | * @return array|bool|string
72 | */
73 | public function getRow()
74 | {
75 | $this->reader->disableAddHeadlineToOutput();
76 |
77 | return $this->reader->readOne();
78 | }
79 |
80 | /**
81 | * @return array
82 | */
83 | public function getAll()
84 | {
85 | $this->reader->enableAddHeadlineToOutput();
86 |
87 | return $this->reader->readAll();
88 | }
89 |
90 | /**
91 | * @return int|null
92 | */
93 | public function getLineNumber()
94 | {
95 | return $this->reader->key();
96 | }
97 | }
--------------------------------------------------------------------------------
/source/Reader/FilteredReader.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-14
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Reader;
8 |
9 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
10 |
11 | class FilteredReader extends Reader
12 | {
13 | /** @var FilterableInterface */
14 | private $filter;
15 |
16 | /**
17 | * @param FilterableInterface $filter
18 | */
19 | public function setFilter(FilterableInterface $filter)
20 | {
21 | $this->filter = $filter;
22 | }
23 |
24 | /**
25 | * @param null|int $lineNumber - if "null", current line number is used
26 | * @return array|bool|string
27 | */
28 | public function readOne($lineNumber = null)
29 | {
30 | $data = parent::readOne($lineNumber);
31 | $filteredData = $this->filter->filter($data);
32 |
33 | if (is_null($filteredData)) {
34 | $filteredData = ($this->valid()) ? $this->readOne() : false;
35 | }
36 |
37 | return $filteredData;
38 | }
39 | }
--------------------------------------------------------------------------------
/source/Reader/FilteredReaderFactory.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-06-24
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Reader;
8 |
9 | use Net\Bazzline\Component\Csv\Filter\PermeableFilter;
10 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
11 |
12 | class FilteredReaderFactory extends ReaderFactory
13 | {
14 | /**
15 | * @return FilteredReader|ReaderInterface
16 | */
17 | protected function getReader()
18 | {
19 | $reader = new FilteredReader();
20 |
21 | $reader->setFilter($this->getFilter());
22 |
23 | return $reader;
24 | }
25 |
26 | /**
27 | * @return FilterableInterface
28 | */
29 | protected function getFilter()
30 | {
31 | return new PermeableFilter();
32 | }
33 | }
--------------------------------------------------------------------------------
/source/Reader/Reader.php:
--------------------------------------------------------------------------------
1 |
4 | * @since: 2015-04-17
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Reader;
8 |
9 | //@see https://github.com/ajgarlag/AjglCsv/blob/master/Reader/ReaderAbstract.php
10 | //@see https://github.com/jwage/easy-csv/blob/master/lib/EasyCSV/Reader.php
11 | //@todo implement save version to call enable/disable headline before setDelimiter etc.
12 | use Net\Bazzline\Component\Csv\AbstractBase;
13 | use Net\Bazzline\Component\Csv\InvalidArgumentException;
14 | use Net\Bazzline\Component\Toolbox\HashMap\Combine;
15 | use SplFileObject;
16 |
17 | class Reader extends AbstractBase implements ReaderInterface
18 | {
19 | /** @var bool */
20 | private $addHeadlineToOutput = true;
21 |
22 | /** @var Combine */
23 | private $combine;
24 |
25 | /** @var int */
26 | private $initialLineNumber = 0;
27 |
28 | /**
29 | * @param null $currentLineNumber
30 | * @return array|bool|string
31 | */
32 | public function __invoke($currentLineNumber = null)
33 | {
34 | return $this->readOne($currentLineNumber);
35 | }
36 |
37 | //begin of AbstractBase
38 | /**
39 | * @param string $delimiter
40 | * @throws InvalidArgumentException
41 | */
42 | public function setDelimiter($delimiter)
43 | {
44 | parent::setDelimiter($delimiter);
45 | if ($this->hasHeadline()) {
46 | $this->enableHasHeadline();
47 | }
48 | }
49 |
50 | /**
51 | * @param string $enclosure
52 | * @throws InvalidArgumentException
53 | */
54 | public function setEnclosure($enclosure)
55 | {
56 | parent::setEnclosure($enclosure);
57 | if ($this->hasHeadline()) {
58 | $this->enableHasHeadline();
59 | }
60 | }
61 |
62 | /**
63 | * @param string $escapeCharacter
64 | * @throws InvalidArgumentException
65 | */
66 | public function setEscapeCharacter($escapeCharacter)
67 | {
68 | parent::setEscapeCharacter($escapeCharacter);
69 | if ($this->hasHeadline()) {
70 | $this->enableHasHeadline();
71 | }
72 | }
73 |
74 | //end of AbstractBase
75 |
76 | //begin of Iterator
77 | /**
78 | * (PHP 5 >= 5.0.0)
79 | * Return the current element
80 | * @link http://php.net/manual/en/iterator.current.php
81 | * @return mixed Can return any type.
82 | */
83 | public function current()
84 | {
85 | return $this->getFileHandler()->current();
86 | }
87 |
88 | /**
89 | * (PHP 5 >= 5.0.0)
90 | * Move forward to next element
91 | * @link http://php.net/manual/en/iterator.next.php
92 | * @return void Any returned value is ignored.
93 | */
94 | public function next()
95 | {
96 | $this->getFileHandler()->next();
97 | }
98 |
99 | /**
100 | * (PHP 5 >= 5.0.0)
101 | * Return the key of the current element
102 | * @link http://php.net/manual/en/iterator.key.php
103 | * @return mixed scalar on success, or null on failure.
104 | */
105 | public function key()
106 | {
107 | return $this->getFileHandler()->key();
108 | }
109 |
110 | /**
111 | * (PHP 5 >= 5.0.0)
112 | * Checks if current position is valid
113 | * @link http://php.net/manual/en/iterator.valid.php
114 | * @return boolean The return value will be casted to boolean and then evaluated.
115 | * Returns true on success or false on failure.
116 | */
117 | public function valid()
118 | {
119 | return $this->getFileHandler()->valid();
120 | }
121 |
122 | /**
123 | * (PHP 5 >= 5.0.0)
124 | * Rewind the Iterator to the first element
125 | * @link http://php.net/manual/en/iterator.rewind.php
126 | * @return void Any returned value is ignored.
127 | */
128 | public function rewind()
129 | {
130 | if ($this->hasHeadline()) {
131 | $this->updateHeadline();
132 | $lineNumber = 1;
133 | } else {
134 | $lineNumber = 0;
135 | }
136 | $this->initialLineNumber = $lineNumber;
137 | $this->seekFileToCurrentLineNumberIfNeeded(
138 | $this->getFileHandler(),
139 | $lineNumber
140 | );
141 | }
142 | //end of Iterator
143 |
144 | //begin of headlines
145 | /**
146 | * @return $this
147 | */
148 | public function disableAddHeadlineToOutput()
149 | {
150 | $this->addHeadlineToOutput = false;
151 |
152 | return $this;
153 | }
154 |
155 | /**
156 | * @return $this
157 | */
158 | public function enableAddHeadlineToOutput()
159 | {
160 | $this->addHeadlineToOutput = true;
161 |
162 | return $this;
163 | }
164 |
165 | /**
166 | * @return $this
167 | */
168 | public function disableHasHeadline()
169 | {
170 | $this->resetHeadline();
171 | $this->rewind();
172 |
173 | return $this;
174 | }
175 |
176 | /**
177 | * @return $this
178 | */
179 | public function enableHasHeadline()
180 | {
181 | $this->updateHeadline();
182 | $this->rewind();
183 |
184 | return $this;
185 | }
186 |
187 | private function updateHeadline()
188 | {
189 | $this->initialLineNumber = 0;
190 | $wasEnabled = $this->addHeadlineToOutput;
191 |
192 | if ($wasEnabled) {
193 | $this->disableAddHeadlineToOutput();
194 | }
195 | $this->setHeadline($this->readOne(0));
196 | if ($wasEnabled) {
197 | $this->enableAddHeadlineToOutput();
198 | }
199 | }
200 |
201 | /**
202 | * @return false|array
203 | */
204 | public function readHeadline()
205 | {
206 | return $this->getHeadline();
207 | }
208 | //end of headlines
209 |
210 | //begin of general
211 | /**
212 | * @param Combine $combine
213 | * @return $this
214 | */
215 | public function setCombine(Combine $combine)
216 | {
217 | $this->combine = $combine;
218 |
219 | return $this;
220 | }
221 |
222 | /**
223 | * @param null|int $lineNumber - if "null", current line number is used
224 | * @return array|bool|string
225 | */
226 | public function readOne($lineNumber = null)
227 | {
228 | $file = $this->getFileHandler();
229 | $headline = $this->getHeadline();
230 | $hasHeadline = $this->hasHeadline();
231 | $this->seekFileToCurrentLineNumberIfNeeded($file, $lineNumber);
232 |
233 | $addHeadline = ($hasHeadline && $this->addHeadlineToOutput && ($this->current() !== false));
234 | $content = ($addHeadline)
235 | ? $this->combine->combine($headline, $this->current())
236 | : $this->current();
237 | $this->next();
238 |
239 | return $content;
240 | }
241 |
242 | /**
243 | * @param int $length
244 | * @param null|int $lineNumberToStartWith - if "null", current line number is used
245 | * @return array
246 | */
247 | public function readMany($length, $lineNumberToStartWith = null)
248 | {
249 | $this->rewind();
250 | $lastLine = $lineNumberToStartWith + $length;
251 | $lines = [];
252 | $currentLine = $lineNumberToStartWith;
253 |
254 | //foreach not usable here since it is calling rewind before iterating
255 | while ($currentLine < $lastLine) {
256 | $line = $this->readOne($currentLine);
257 | $lines = $this->addToLinesIfLineIsValid($lines, $line);
258 | if (!$this->valid()) {
259 | $currentLine = $lastLine;
260 | }
261 | ++$currentLine;
262 | }
263 |
264 | return $lines;
265 | }
266 |
267 | /**
268 | * @return array
269 | */
270 | public function readAll()
271 | {
272 | $this->rewind();
273 | $lines = [];
274 |
275 | while ($line = $this()) {
276 | $lines = $this->addToLinesIfLineIsValid($lines, $line);
277 | }
278 |
279 | return $lines;
280 | }
281 | //end of general
282 |
283 | /**
284 | * @return string
285 | */
286 | protected function getFileHandlerOpenMode()
287 | {
288 | return 'r';
289 | }
290 |
291 | /**
292 | * @param array $lines
293 | * @param mixed $line
294 | * @return array
295 | */
296 | private function addToLinesIfLineIsValid(array &$lines, $line)
297 | {
298 | if (!is_null($line)) {
299 | $lines[] = $line;
300 | }
301 |
302 | return $lines;
303 | }
304 |
305 | /**
306 | * @param SplFileObject $file
307 | * @param null|int $newLineNumber
308 | * @return SplFileObject
309 | */
310 | private function seekFileToCurrentLineNumberIfNeeded(SplFileObject $file, $newLineNumber = null)
311 | {
312 | $seekIsNeeded = ((!is_null($newLineNumber))
313 | && ($newLineNumber >= $this->initialLineNumber)
314 | && ($newLineNumber !== $this->key()));
315 |
316 | if ($seekIsNeeded) {
317 | $file->seek($newLineNumber);
318 | }
319 |
320 | return $file;
321 | }
322 | }
323 |
--------------------------------------------------------------------------------
/source/Reader/ReaderFactory.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-06
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Reader;
8 |
9 | use Net\Bazzline\Component\Csv\AbstractFactory;
10 | use Net\Bazzline\Component\Toolbox\HashMap\Combine;
11 |
12 | class ReaderFactory extends AbstractFactory
13 | {
14 | /**
15 | * @return object|Reader|ReaderInterface
16 | */
17 | public function create()
18 | {
19 | $reader = $this->getReader();
20 |
21 | $reader->setCombine($this->getCombine());
22 | $reader->setDelimiter($this->getDelimiter());
23 | $reader->setEnclosure($this->getEnclosure());
24 | $reader->setEscapeCharacter($this->getEscapeCharacter());
25 |
26 | return $reader;
27 | }
28 |
29 | /**
30 | * @return Combine
31 | */
32 | protected function getCombine()
33 | {
34 | return new Combine();
35 | }
36 |
37 | /**
38 | * @return Reader|ReaderInterface
39 | */
40 | protected function getReader()
41 | {
42 | return new Reader();
43 | }
44 | }
--------------------------------------------------------------------------------
/source/Reader/ReaderInterface.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-09-09
5 | */
6 | namespace Net\Bazzline\Component\Csv\Reader;
7 |
8 | use Iterator;
9 | use Net\Bazzline\Component\Csv\BaseInterface;
10 | use Net\Bazzline\Component\Toolbox\HashMap\Combine;
11 |
12 | /**
13 | * Interface ReaderInterface
14 | * @package Net\Bazzline\Component\Csv\Reader
15 | */
16 | interface ReaderInterface extends BaseInterface, Iterator
17 | {
18 | /**
19 | * @return $this
20 | */
21 | public function disableAddHeadlineToOutput();
22 |
23 | /**
24 | * @return $this
25 | */
26 | public function enableAddHeadlineToOutput();
27 |
28 | /**
29 | * @return $this
30 | */
31 | public function disableHasHeadline();
32 |
33 | /**
34 | * @return $this
35 | */
36 | public function enableHasHeadline();
37 |
38 | /**
39 | * @return false|array
40 | */
41 | public function readHeadline();
42 |
43 | /**
44 | * @param Combine $combine
45 | * @return $this
46 | */
47 | public function setCombine(Combine $combine);
48 |
49 | /**
50 | * @param null|int $lineNumber - if "null", current line number is used
51 | * @return array|bool|string
52 | */
53 | public function readOne($lineNumber = null);
54 |
55 | /**
56 | * @param int $length
57 | * @param null|int $lineNumberToStartWith - if "null", current line number is used
58 | * @return array
59 | */
60 | public function readMany($length, $lineNumberToStartWith = null);
61 |
62 | /**
63 | * @return array
64 | */
65 | public function readAll();
66 | }
--------------------------------------------------------------------------------
/source/RuntimeException.php:
--------------------------------------------------------------------------------
1 |
4 | * @since: 2015-04-17
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv;
8 |
9 | use RuntimeException as ParentClass;
10 |
11 | class RuntimeException extends ParentClass {}
--------------------------------------------------------------------------------
/source/Writer/EasyCsvWriterAdapter.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-16
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | class EasyCsvWriterAdapter
10 | {
11 | /** @var Writer|WriterForPhp5Dot3|WriterInterface */
12 | private $writer;
13 |
14 | /**
15 | * @param string $path
16 | * @param string $mode - is not used
17 | * @param null|WriterInterface $writer - optional
18 | */
19 | public function __construct($path, $mode = 'r+', WriterInterface $writer = null)
20 | {
21 | if (is_null($writer)) {
22 | $factory = new WriterFactory();
23 | $this->writer = $factory->create();
24 | } else {
25 | $this->writer = $writer;
26 | }
27 |
28 | $this->writer->setDelimiter(',');
29 | $this->writer->setEnclosure('"');
30 | $this->writer->setPath($path);
31 | }
32 |
33 | /**
34 | * @param string $delimiter
35 | */
36 | public function setDelimiter($delimiter)
37 | {
38 | $this->writer->setDelimiter($delimiter);
39 | }
40 |
41 | /**
42 | * @param string $enclosure
43 | */
44 | public function setEnclosure($enclosure)
45 | {
46 | $this->writer->setEnclosure($enclosure);
47 | }
48 |
49 | /**
50 | * @param mixed $row
51 | * @return false|int
52 | */
53 | public function writeRow($row)
54 | {
55 | return $this->writer->writeOne($row);
56 | }
57 |
58 | /**
59 | * @param array $array
60 | */
61 | public function writeFromArray(array $array)
62 | {
63 | $this->writer->writeMany($array);
64 | }
65 | }
--------------------------------------------------------------------------------
/source/Writer/FilteredWriter.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-14
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
10 |
11 | class FilteredWriter extends Writer
12 | {
13 | /** @var FilterableInterface */
14 | private $filter;
15 |
16 | /**
17 | * @param FilterableInterface $filter
18 | */
19 | public function setFilter(FilterableInterface $filter)
20 | {
21 | $this->filter = $filter;
22 | }
23 |
24 | /**
25 | * @param array|mixed $data
26 | * @return false|int
27 | */
28 | public function writeOne($data)
29 | {
30 | $filteredData = $this->filter->filter($data);
31 |
32 | return (!is_null($filteredData))
33 | ? parent::writeOne($filteredData) : false;
34 | }
35 | }
--------------------------------------------------------------------------------
/source/Writer/FilteredWriterFactory.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-14
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | use Net\Bazzline\Component\Csv\Filter\PermeableFilter;
10 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
11 |
12 | class FilteredWriterFactory extends WriterFactory
13 | {
14 | /**
15 | * @return FilteredWriter|FilteredWriterForPhp3Dot3
16 | */
17 | protected function getWriter()
18 | {
19 | if ($this->phpVersionLessThen5Dot4()) {
20 | $writer = new FilteredWriterForPhp3Dot3();
21 | } else {
22 | $writer = new FilteredWriter();
23 | }
24 |
25 | $writer->setFilter($this->getFilter());
26 |
27 | return $writer;
28 | }
29 |
30 | /**
31 | * @return FilterableInterface
32 | */
33 | protected function getFilter()
34 | {
35 | return new PermeableFilter();
36 | }
37 | }
--------------------------------------------------------------------------------
/source/Writer/FilteredWriterForPhp3Dot3.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-14
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
10 |
11 | class FilteredWriterForPhp3Dot3 extends WriterForPhp5Dot3
12 | {
13 | /** @var FilterableInterface */
14 | private $filter;
15 |
16 | /**
17 | * @param FilterableInterface $filter
18 | */
19 | public function setFilter(FilterableInterface $filter)
20 | {
21 | $this->filter = $filter;
22 | }
23 |
24 | /**
25 | * @param array|mixed $data
26 | * @return false|int
27 | */
28 | public function writeOne($data)
29 | {
30 | $filteredData = $this->filter->filter($data);
31 |
32 | return (!is_null($filteredData))
33 | ? parent::writeOne($filteredData) : false;
34 | }
35 | }
--------------------------------------------------------------------------------
/source/Writer/Writer.php:
--------------------------------------------------------------------------------
1 |
4 | * @since: 2015-04-17
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | use Net\Bazzline\Component\Csv\AbstractBase;
10 | use Net\Bazzline\Component\Csv\InvalidArgumentException;
11 |
12 | /**
13 | * Class Writer
14 | * @package Net\Bazzline\Component\Csv\Writer
15 | */
16 | class Writer extends AbstractBase implements WriterInterface
17 | {
18 | const OPEN_MODE_APPEND = 'a';
19 | const OPEN_MODE_TRUNCATE = 'w';
20 |
21 | /** @var boolean */
22 | private $useTruncateAsOpenMode = false;
23 |
24 | /**
25 | * @param mixed|array $data
26 | * @return false|int
27 | */
28 | public function __invoke($data)
29 | {
30 | return $this->writeOne($data);
31 | }
32 |
33 |
34 | //begin of general
35 | /**
36 | * @param string $path
37 | * @param bool $setPathAsCurrentPath
38 | * @return bool
39 | * @throws InvalidArgumentException
40 | * @todo implement path validation
41 | */
42 | public function copy($path, $setPathAsCurrentPath = false)
43 | {
44 | $couldBeCopied = copy($this->getPath(), $path);
45 |
46 | if ($setPathAsCurrentPath) {
47 | if ($couldBeCopied) {
48 | $this->close();
49 | $this->setPath($path);
50 | }
51 | }
52 |
53 | return $couldBeCopied;
54 | }
55 |
56 | /**
57 | * @param string $path
58 | * @return bool
59 | * @todo implement path validation
60 | */
61 | public function move($path)
62 | {
63 | $couldBeMoved = rename($this->getPath(), $path);
64 |
65 | if ($couldBeMoved) {
66 | $this->close();
67 | $this->setPath($path);
68 | }
69 |
70 | return $couldBeMoved;
71 | }
72 |
73 | /**
74 | * @return bool
75 | */
76 | public function delete()
77 | {
78 | $this->close();
79 |
80 | return unlink($this->getPath());
81 | }
82 |
83 | public function truncate()
84 | {
85 | $this->close();
86 | $this->useTruncateAsOpenMode = true;
87 | $this->open($this->getPath());
88 | $this->useTruncateAsOpenMode = false;
89 | }
90 |
91 | /**
92 | * truncates file and writes content
93 | *
94 | * @param array $collection
95 | * @return false|int
96 | */
97 | public function writeAll(array $collection)
98 | {
99 | $this->truncate();
100 |
101 | return $this->writeMany($collection);
102 | }
103 |
104 | /**
105 | * @param array $headlines
106 | * @return false|int
107 | */
108 | public function writeHeadlines(array $headlines)
109 | {
110 | $this->setHeadline($headlines);
111 |
112 | return $this->writeOne($headlines);
113 | }
114 |
115 | /**
116 | * @param array $collection
117 | * @return false|int
118 | */
119 | public function writeMany(array $collection)
120 | {
121 | $lengthOfTheWrittenStrings = 0;
122 |
123 | foreach ($collection as $data) {
124 | $lengthOfTheWrittenString = $this->writeOne($data);
125 |
126 | if ($lengthOfTheWrittenString === false) {
127 | $lengthOfTheWrittenStrings = $lengthOfTheWrittenString;
128 | break;
129 | } else {
130 | $lengthOfTheWrittenStrings += $lengthOfTheWrittenString;
131 | }
132 | }
133 |
134 | return $lengthOfTheWrittenStrings;
135 | }
136 |
137 | /**
138 | * @param string|array $data
139 | * @return false|int
140 | */
141 | public function writeOne($data)
142 | {
143 | $data = $this->convertToArrayIfNeeded($data);
144 |
145 | return $this->getFileHandler()->fputcsv($data, $this->getDelimiter(), $this->getEnclosure());
146 | }
147 | //end of general
148 |
149 | /**
150 | * @param string|array $data
151 | * @return array
152 | */
153 | protected function convertToArrayIfNeeded($data)
154 | {
155 | if (!is_array($data)) {
156 | $data = explode($this->getDelimiter(), $data);
157 | $data = array_map(function($value) {
158 | return trim($value);
159 | }, $data);
160 | }
161 |
162 | return $data;
163 | }
164 |
165 | /**
166 | * @return string
167 | */
168 | protected function getFileHandlerOpenMode()
169 | {
170 | return ($this->useTruncateAsOpenMode) ? self::OPEN_MODE_TRUNCATE : self::OPEN_MODE_APPEND;
171 | }
172 | }
--------------------------------------------------------------------------------
/source/Writer/WriterFactory.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-06
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | use Net\Bazzline\Component\Csv\AbstractFactory;
10 |
11 | class WriterFactory extends AbstractFactory
12 | {
13 | /**
14 | * @return Writer|WriterForPhp5Dot3|WriterInterface
15 | */
16 | public function create()
17 | {
18 | $writer = $this->getWriter();
19 |
20 | $writer->setDelimiter($this->getDelimiter());
21 | $writer->setEnclosure($this->getEnclosure());
22 | $writer->setEscapeCharacter($this->getEscapeCharacter());
23 |
24 | return $writer;
25 | }
26 |
27 | /**
28 | * @return Writer|WriterForPhp5Dot3|WriterInterface
29 | */
30 | protected function getWriter()
31 | {
32 | if ($this->phpVersionLessThen5Dot4()) {
33 | $writer = new WriterForPhp5Dot3();
34 | } else {
35 | $writer = new Writer();
36 | }
37 |
38 | return $writer;
39 | }
40 |
41 | /**
42 | * @return boolean
43 | */
44 | protected function phpVersionLessThen5Dot4()
45 | {
46 | return (version_compare(phpversion(), '5.4', '<'));
47 | }
48 | }
--------------------------------------------------------------------------------
/source/Writer/WriterForPhp5Dot3.php:
--------------------------------------------------------------------------------
1 |
4 | * @since: 2015-05-13
5 | */
6 |
7 | namespace Net\Bazzline\Component\Csv\Writer;
8 |
9 | /**
10 | * Class WriterForPhp5Dot3
11 | * @package Net\Bazzline\Component\Csv\Writer
12 | */
13 | class WriterForPhp5Dot3 extends Writer
14 | {
15 | public function __destruct()
16 | {
17 | $this->close();
18 | }
19 |
20 | /**
21 | * @param mixed|array $data
22 | * @return false|int
23 | */
24 | public function writeOne($data)
25 | {
26 | $data = $this->convertToArrayIfNeeded($data);
27 |
28 | return fputcsv($this->getFileHandler(), $data, $this->getDelimiter(), $this->getEnclosure());
29 | }
30 |
31 | /**
32 | * @param string $path
33 | * @return resource
34 | */
35 | protected function open($path)
36 | {
37 | $fileHandler = fopen($path, $this->getFileHandlerOpenMode());
38 |
39 | return $fileHandler;
40 | }
41 | }
--------------------------------------------------------------------------------
/source/Writer/WriterInterface.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-09-09
5 | */
6 | namespace Net\Bazzline\Component\Csv\Writer;
7 |
8 | use Net\Bazzline\Component\Csv\BaseInterface;
9 |
10 | /**
11 | * Interface WriterInterface
12 | * @package Net\Bazzline\Component\Csv\Writer
13 | */
14 | interface WriterInterface extends BaseInterface
15 | {
16 | /**
17 | * @param mixed|array $data
18 | * @return false|int
19 | */
20 | public function __invoke($data);
21 |
22 |
23 | /**
24 | * @param string $path
25 | * @param bool $setPathAsCurrentPath
26 | * @return bool
27 | * @throws \InvalidArgumentException
28 | */
29 | public function copy($path, $setPathAsCurrentPath = false);
30 |
31 | /**
32 | * @param string $path
33 | * @return bool
34 | */
35 | public function move($path);
36 |
37 | /**
38 | * @return bool
39 | */
40 | public function delete();
41 |
42 | public function truncate();
43 |
44 | /**
45 | * truncates file and writes content
46 | *
47 | * @param array $collection
48 | * @return false|int
49 | */
50 | public function writeAll(array $collection);
51 |
52 | /**
53 | * @param array $headlines
54 | * @return false|int
55 | */
56 | public function writeHeadlines(array $headlines);
57 |
58 | /**
59 | * @param array $collection
60 | * @return false|int
61 | */
62 | public function writeMany(array $collection);
63 |
64 | /**
65 | * @param string|array $data
66 | * @return false|int
67 | */
68 | public function writeOne($data);
69 | }
--------------------------------------------------------------------------------
/test/AbstractTestCase.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-04-24
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | use Mockery;
10 | use Net\Bazzline\Component\Csv\Reader\FilteredReader;
11 | use Net\Bazzline\Component\Csv\Reader\FilteredReaderFactory;
12 | use Net\Bazzline\Component\Csv\Reader\Reader;
13 | use Net\Bazzline\Component\Csv\Reader\ReaderFactory;
14 | use Net\Bazzline\Component\Csv\Reader\ReaderInterface;
15 | use Net\Bazzline\Component\Csv\Writer\FilteredWriter;
16 | use Net\Bazzline\Component\Csv\Writer\FilteredWriterFactory;
17 | use Net\Bazzline\Component\Csv\Writer\Writer;
18 | use Net\Bazzline\Component\Csv\Writer\WriterFactory;
19 | use Net\Bazzline\Component\Csv\Writer\WriterInterface;
20 | use Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface;
21 | use org\bovigo\vfs\vfsStream;
22 | use PHPUnit\Framework\TestCase;
23 |
24 | abstract class AbstractTestCase extends TestCase
25 | {
26 | /** @var FilteredReaderFactory */
27 | private $filteredReaderFactory;
28 |
29 | /** @var FilteredWriterFactory */
30 | private $filteredWriterFactory;
31 |
32 | /** @var string */
33 | private $path;
34 |
35 | /** @var array */
36 | private $pathOfFiles;
37 |
38 | /** @var ReaderFactory */
39 | private $readerFactory;
40 |
41 | /** @var WriterFactory */
42 | private $writerFactory;
43 |
44 | /**
45 | * Constructs a test case with the given name.
46 | *
47 | * @param string $name
48 | * @param array $data
49 | * @param string $dataName
50 | */
51 | public function __construct($name = null, array $data = [], $dataName = '')
52 | {
53 | parent::__construct($name, $data, $dataName);
54 |
55 | $this->filteredReaderFactory = new FilteredReaderFactory();
56 | $this->filteredWriterFactory = new FilteredWriterFactory();
57 | $this->path = __DIR__ . DIRECTORY_SEPARATOR . 'data';
58 | $this->pathOfFiles = [];
59 | $this->readerFactory = new ReaderFactory();
60 | $this->writerFactory = new WriterFactory();
61 | }
62 |
63 | public function __destruct()
64 | {
65 | foreach ($this->pathOfFiles as $path) {
66 | if (file_exists($path)) {
67 | unlink($path);
68 | }
69 | }
70 | Mockery::close();
71 | }
72 |
73 | /**
74 | * @param int $permissions
75 | * @param string $path
76 | * @return \org\bovigo\vfs\vfsStreamDirectory
77 | */
78 | protected function createFilesystem($permissions = 0700, $path = 'root')
79 | {
80 | return vfsStream::setup($path, $permissions);
81 | }
82 |
83 | /**
84 | * @param string $name
85 | * @param int $permissions
86 | * @return \org\bovigo\vfs\vfsStreamFile
87 | */
88 | protected function createFile($name = 'test.csv', $permissions = 0700)
89 | {
90 | return vfsStream::newFile($name, $permissions);
91 | }
92 |
93 | /**
94 | * @param string $name
95 | * @return string
96 | */
97 | protected function createRealFilePath($name)
98 | {
99 | $path = $this->path . DIRECTORY_SEPARATOR . $name;
100 | $this->pathOfFiles[] = $path;
101 |
102 | return $path;
103 | }
104 |
105 | /**
106 | * @return FilteredReader
107 | */
108 | protected function createFilteredReader()
109 | {
110 | return $this->filteredReaderFactory->create();
111 | }
112 |
113 | /**
114 | * @return ReaderInterface
115 | */
116 | protected function createReader()
117 | {
118 | return $this->readerFactory->create();
119 | }
120 |
121 |
122 |
123 | /**
124 | * @return \Mockery\MockInterface|FilterableInterface
125 | */
126 | protected function createFilter()
127 | {
128 | return Mockery::mock('Net\Bazzline\Component\GenericAgreement\Data\FilterableInterface');
129 | }
130 |
131 | /**
132 | * @return FilteredWriter
133 | */
134 | protected function createFilteredWriter()
135 | {
136 | return $this->filteredWriterFactory->create();
137 | }
138 |
139 | /**
140 | * @return Writer|WriterInterface
141 | */
142 | protected function createWriter()
143 | {
144 | return $this->writerFactory->create();
145 | }
146 |
147 | /**
148 | * @return boolean
149 | */
150 | protected function phpVersionLessThen5Dot4()
151 | {
152 | return (version_compare(phpversion(), '5.4', '<'));
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/test/EasyCsvReaderAdapterTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-17
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | use Net\Bazzline\Component\Csv\Reader\EasyCsvReaderAdapter;
10 |
11 | /**
12 | * Class EasyCsvReaderAdapterTest
13 | * @package Test\Net\Bazzline\Component\Csv
14 | * @see https://github.com/jwage/easy-csv/blob/0.0.1/tests/EasyCSV/Tests/ReaderTest.php
15 | */
16 | class EasyCsvReaderAdapterTest extends AbstractTestCase
17 | {
18 | /**
19 | * @dataProvider getReaders
20 | * @param EasyCsvReaderAdapter $reader
21 | */
22 | public function testOneAtATime(EasyCsvReaderAdapter $reader)
23 | {
24 | while ($row = $reader->getRow()) {
25 | $this->assertTrue(is_array($row));
26 | $this->assertEquals(3, count($row));
27 | }
28 | }
29 |
30 | /**
31 | * @dataProvider getReaders
32 | * @param EasyCsvReaderAdapter $reader
33 | */
34 | public function testGetAll(EasyCsvReaderAdapter $reader)
35 | {
36 | $this->assertEquals(5, count($reader->getAll()));
37 | }
38 |
39 | /**
40 | * @dataProvider getReaders
41 | * @param EasyCsvReaderAdapter $reader
42 | */
43 | public function testGetHeaders(EasyCsvReaderAdapter $reader)
44 | {
45 | $this->assertEquals(
46 | [
47 | "column1",
48 | "column2",
49 | "column3"
50 | ],
51 | $reader->getHeaders()
52 | );
53 | }
54 |
55 | /**
56 | * @return array
57 | */
58 | public function getReaders()
59 | {
60 | $content =
61 | '"column1", "column2", "column3"' . PHP_EOL .
62 | '"1column2value", "1column3value", "1column4value"' . PHP_EOL .
63 | '"2column2value", "2column3value", "2column4value"' . PHP_EOL .
64 | '"3column2value", "3column3value", "3column4value"' . PHP_EOL .
65 | '"4column2value", "4column3value", "4column4value"' . PHP_EOL .
66 | '"5column2value", "5column3value", "5column4value"';
67 | $contentWithSemicolonAsDelimiter =
68 | '"column1"; "column2"; "column3"' . PHP_EOL .
69 | '"1column2value"; "1column3value"; "1column4value"' . PHP_EOL .
70 | '"2column2value"; "2column3value"; "2column4value"' . PHP_EOL .
71 | '"3column2value"; "3column3value"; "3column4value"' . PHP_EOL .
72 | '"4column2value"; "4column3value"; "4column4value"' . PHP_EOL .
73 | '5column2value"; "5column3value"; "5column4value"';
74 |
75 | if ($this->phpVersionLessThen5Dot4()) {
76 | $path = $this->createRealFilePath('read.csv');
77 | $pathWithSemicolonAsDelimiter = $this->createRealFilePath('read_cs.csv');
78 |
79 | file_put_contents($path, $content);
80 | file_put_contents($pathWithSemicolonAsDelimiter, $contentWithSemicolonAsDelimiter);
81 |
82 | $reader = new EasyCsvReaderAdapter($path);
83 | $readerWithSemicolonAsDelimiter = new EasyCsvReaderAdapter($pathWithSemicolonAsDelimiter);
84 | } else {
85 | $file = $this->createFile('read.csv');
86 | $filesystem = $this->createFilesystem();
87 | $fileWithSemicolonAsDelimiter = $this->createFile('read_sc.csv');
88 |
89 | $file->setContent($content);
90 | $fileWithSemicolonAsDelimiter->setContent($contentWithSemicolonAsDelimiter);
91 | $filesystem->addChild($file);
92 | $filesystem->addChild($fileWithSemicolonAsDelimiter);
93 |
94 | $reader = new EasyCsvReaderAdapter($file->url());
95 | $readerWithSemicolonAsDelimiter = new EasyCsvReaderAdapter($fileWithSemicolonAsDelimiter->url());
96 | }
97 |
98 | $readerWithSemicolonAsDelimiter->setDelimiter(';');
99 |
100 | return [
101 | [
102 | $reader
103 | ],
104 | [
105 | $readerWithSemicolonAsDelimiter
106 | ]
107 | ];
108 | }
109 |
110 |
111 | public function testReadWrittenFile()
112 | {
113 | $content = 'column1,column2,column3' . PHP_EOL .
114 | '1test1,"1test2ing this out",1test3' . PHP_EOL .
115 | '2test1,"2test2 ing this out ok",2test3' . PHP_EOL;
116 | $file = $this->createFile('write.csv');
117 | $filesystem = $this->createFilesystem();
118 |
119 | $file->setContent($content);
120 | $filesystem->addChild($file);
121 |
122 | $reader = new EasyCsvReaderAdapter($file->url());
123 |
124 | $results = $reader->getAll();
125 | $expected = [
126 | [
127 | 'column1' => '1test1',
128 | 'column2' => '1test2ing this out',
129 | 'column3' => '1test3'
130 | ],
131 | [
132 | 'column1' => '2test1',
133 | 'column2' => '2test2 ing this out ok',
134 | 'column3' => '2test3'
135 | ]
136 | ];
137 |
138 | $this->assertEquals($expected, $results);
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/test/EasyCsvWriterAdapterTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-17
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | use Net\Bazzline\Component\Csv\Writer\EasyCsvWriterAdapter;
10 |
11 | /**
12 | * Class EasyCsvWriterAdapterTest
13 | * @package Test\Net\Bazzline\Component\Csv
14 | * @see https://github.com/jwage/easy-csv/blob/0.0.1/tests/EasyCSV/Tests/WriterTest.php
15 | */
16 | class EasyCsvWriterAdapterTest extends AbstractTestCase
17 | {
18 | public function testWriteRow()
19 | {
20 | $file = $this->createFile('write.csv');
21 | $filesystem = $this->createFilesystem();
22 | $filesystem->addChild($file);
23 | $writer = new EasyCsvWriterAdapter($file->url());
24 |
25 | $expectedContent = 'test1,test2,test3' . PHP_EOL;
26 | $line = 'test1, test2, test3';
27 |
28 | $writer->writeRow($line);
29 |
30 | $this->assertEquals($expectedContent, $file->getContent());
31 | }
32 |
33 | public function testWriteFromArray()
34 | {
35 | $file = $this->createFile('write.csv');
36 | $filesystem = $this->createFilesystem();
37 | $filesystem->addChild($file);
38 | $writer = new EasyCsvWriterAdapter($file->url());
39 |
40 | $array = array(
41 | '1test1, 1test2ing this out, 1test3',
42 | array(
43 | '2test1', '2test2 ing this out ok', '2test3'
44 | )
45 | );
46 | $line = 'column1, column2, column3';
47 | $expectedContent = 'column1,column2,column3' . PHP_EOL .
48 | '1test1,"1test2ing this out",1test3' . PHP_EOL .
49 | '2test1,"2test2 ing this out ok",2test3' . PHP_EOL;
50 |
51 | $writer->writeRow($line);
52 | $writer->writeFromArray($array);
53 |
54 | $this->assertEquals($expectedContent, $file->getContent());
55 | }
56 | }
--------------------------------------------------------------------------------
/test/FilteredReaderTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-06-22
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | //@todo implement call of this tests with different delimiters etc. (after the
10 | //setters are developed
11 | //@todo implement writeOne(!array)
12 | class FilteredReaderTest extends ReaderTest
13 | {
14 | public function testReadContentWithAlwaysInvalidFilter()
15 | {
16 | $file = $this->createFile();
17 | $filesystem = $this->createFilesystem();
18 | $filter = $this->createFilter();
19 | $reader = $this->createFilteredReader();
20 |
21 | $file->setContent($this->getContentAsString());
22 | $filesystem->addChild($file);
23 | $filter->shouldReceive('filter')
24 | ->andReturn(null);
25 |
26 | $reader->setFilter($filter);
27 | $reader->setPath($file->url());
28 |
29 | $this->assertFalse($reader->readOne());
30 | $this->assertFalse($reader());
31 | $this->assertEquals([], $reader->readAll());
32 | }
33 |
34 | public function testReadAllPassingSecondRowAsValidFilter()
35 | {
36 | $lineNumberOfContent = 1;
37 | $content = $this->contentAsArray;
38 | $expectedContent = [
39 | $content[
40 | $lineNumberOfContent
41 | ]
42 | ];
43 | $file = $this->createFile();
44 | $filesystem = $this->createFilesystem();
45 | $filter = $this->createFilter();
46 | $reader = $this->createFilteredReader();
47 |
48 | $file->setContent($this->getContentAsString());
49 | $filesystem->addChild($file);
50 | $filter->shouldReceive('filter')
51 | ->andReturn(null, $expectedContent[0], null, null);
52 |
53 | $reader->setFilter($filter);
54 | $reader->setPath($file->url());
55 |
56 | $this->assertEquals($expectedContent, $reader->readAll());
57 | }
58 |
59 | public function testReadManyPassingSecondRowAsValidFilter()
60 | {
61 | $content = $this->contentAsArray;
62 | $expectedContent = [];
63 | $length = 2;
64 | $file = $this->createFile();
65 | $filesystem = $this->createFilesystem();
66 | $filter = $this->createFilter();
67 | $reader = $this->createFilteredReader();
68 | $start = 2;
69 |
70 | $file->setContent($this->getContentAsString());
71 | $filesystem->addChild($file);
72 |
73 | //generating expected content
74 | $end = $start + $length;
75 | $counter = ($start + 1); //+1 because of the first false from the filter
76 |
77 | while ($counter < $end) {
78 | $expectedContent[] = $content[$counter];
79 | ++$counter;
80 | }
81 |
82 | $filter->shouldReceive('filter')
83 | ->andReturn(null, $expectedContent[0]);
84 |
85 | $reader->setFilter($filter);
86 | $reader->setPath($file->url());
87 |
88 | $this->assertEquals($expectedContent, $reader->readMany($length, $start));
89 | }
90 |
91 | public function testReadOnePassingSecondRowAsValidFilter()
92 | {
93 | $lineNumberOfContent = 1;
94 | $content = $this->contentAsArray;
95 | $expectedContent = $content[$lineNumberOfContent];
96 | $file = $this->createFile();
97 | $filesystem = $this->createFilesystem();
98 | $filter = $this->createFilter();
99 | $reader = $this->createFilteredReader();
100 |
101 | $file->setContent($this->getContentAsString());
102 | $filesystem->addChild($file);
103 | $filter->shouldReceive('filter')
104 | ->andReturn(null, $expectedContent);
105 |
106 | $reader->setFilter($filter);
107 | $reader->setPath($file->url());
108 |
109 | $this->assertEquals($expectedContent, $reader());
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/test/FilteredWriterTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-06-22
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | //@todo implement call of this tests with different delimiters etc. (after the
10 | //setters are developed
11 | //@todo implement writeOne(!array)
12 | class FilteredWriterTest extends WriterTest
13 | {
14 | public function testWriteContentLinePerLineUsingWriteOneAndAlwaysInvalidFilter()
15 | {
16 | $delimiters = $this->delimiters;
17 |
18 | foreach ($delimiters as $delimiter) {
19 | $collection = $this->contentAsArray;
20 | $expectedContent = null;
21 | $filter = $this->createFilter();
22 | $file = $this->createFile();
23 | $filesystem = $this->createFilesystem();
24 | $writer = $this->createFilteredWriter();
25 |
26 | $filter->shouldReceive('filter')
27 | ->andReturn(null);
28 | $filesystem->addChild($file);
29 |
30 | $writer->setDelimiter($delimiter);
31 | $writer->setFilter($filter);
32 | $writer->setPath($file->url());
33 |
34 | foreach ($collection as $content) {
35 | $this->assertFalse($writer->writeOne($content));
36 | }
37 |
38 | $this->assertEquals($expectedContent, $file->getContent());
39 | }
40 | }
41 | public function testWriteContentLinePerLineUsingWriteOneAndPassingSecondRowAsValidFilter()
42 | {
43 | $delimiters = $this->delimiters;
44 | $lineNumberOfContent = 1;
45 |
46 | foreach ($delimiters as $delimiter) {
47 | $collection = $this->contentAsArray;
48 | $expectedContent = $this->convertArrayToStrings(
49 | [
50 | $collection[
51 | $lineNumberOfContent
52 | ]
53 | ],
54 | $delimiter
55 | );
56 | $filter = $this->createFilter();
57 | $file = $this->createFile();
58 | $filesystem = $this->createFilesystem();
59 | $writer = $this->createFilteredWriter();
60 |
61 | $filter->shouldReceive('filter')
62 | ->andReturn(null, $expectedContent, null, null);
63 | $filesystem->addChild($file);
64 |
65 | $writer->setDelimiter($delimiter);
66 | $writer->setFilter($filter);
67 | $writer->setPath($file->url());
68 |
69 | foreach ($collection as $index => $content) {
70 | if ($index === $lineNumberOfContent) {
71 | $this->assertNotFalse($writer->writeOne($content));
72 | } else {
73 | $this->assertFalse($writer->writeOne($content));
74 | }
75 | }
76 |
77 | $this->assertEquals($expectedContent, $file->getContent());
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/test/ReaderTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-04-24
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | //@todo implement call of this tests with different delimiters etc. (after the
10 | //setters are developed
11 | //@todo implement tests with headlines enabled for each situation
12 | class ReaderTest extends AbstractTestCase
13 | {
14 | /**
15 | * @var array
16 | */
17 | protected $contentAsArray = [
18 | [
19 | 'headlines foo',
20 | 'headlines bar'
21 | ],
22 | [
23 | 'foo',
24 | 'bar'
25 | ],
26 | [
27 | 'foobar',
28 | 'baz'
29 | ],
30 | [
31 | 'baz',
32 | 'barfoo'
33 | ]
34 | ];
35 |
36 | public function testHasHeadline()
37 | {
38 | $content = $this->contentAsArray;
39 | $file = $this->createFile();
40 | $filesystem = $this->createFilesystem();
41 | $reader = $this->createReader();
42 |
43 | $expectedContent = array_slice($content, 1);
44 | $expectedHeadline = $content[0];
45 | $expectedContent = $this->addHeadlineAsKeysToContent($expectedHeadline, $expectedContent);
46 |
47 | $file->setContent($this->getContentAsString());
48 | $filesystem->addChild($file);
49 | $reader->setPath($file->url());
50 | $reader->enableHasHeadline();
51 |
52 | $this->assertTrue($reader->hasHeadline(), 'has headline');
53 | $this->assertEquals($expectedContent, $reader->readAll(), 'read all');
54 | $this->assertEquals($expectedHeadline, $reader->readHeadline(), 'read headline');
55 | }
56 |
57 | public function testReadWholeContentAtOnce()
58 | {
59 | $file = $this->createFile();
60 | $filesystem = $this->createFilesystem();
61 | $reader = $this->createReader();
62 |
63 | $file->setContent($this->getContentAsString());
64 | $filesystem->addChild($file);
65 | $reader->setPath($file->url());
66 |
67 | $this->assertFalse($reader->hasHeadline());
68 | $this->assertEquals($this->contentAsArray, $reader->readAll());
69 | }
70 |
71 | public function testReadWholeContentByUsingTheIteratorInterface()
72 | {
73 | $file = $this->createFile();
74 | $filesystem = $this->createFilesystem();
75 | $reader = $this->createReader();
76 |
77 | $file->setContent($this->getContentAsString());
78 | $filesystem->addChild($file);
79 | $reader->setPath($file->url());
80 |
81 | $index = 0;
82 | foreach ($reader as $line) {
83 | $this->assertEquals($this->contentAsArray[$index], $line);
84 | ++$index;
85 | }
86 | }
87 |
88 | public function testReadWholeContentByUsingReaderAsAFunction()
89 | {
90 | $file = $this->createFile();
91 | $filesystem = $this->createFilesystem();
92 | $reader = $this->createReader();
93 |
94 | $file->setContent($this->getContentAsString());
95 | $filesystem->addChild($file);
96 | $reader->setPath($file->url());
97 |
98 | $index = 0;
99 |
100 | while ($line = $reader()) {
101 | $this->assertEquals($this->contentAsArray[$index], $line);
102 | ++$index;
103 | }
104 | }
105 |
106 | public function testReadWholeContentLinePerLine()
107 | {
108 | $file = $this->createFile();
109 | $filesystem = $this->createFilesystem();
110 | $reader = $this->createReader();
111 |
112 | $file->setContent($this->getContentAsString());
113 | $filesystem->addChild($file);
114 | $reader->setPath($file->url());
115 |
116 | $index = 0;
117 |
118 | while ($line = $reader->readOne()) {
119 | $this->assertEquals($this->contentAsArray[$index], $line);
120 | ++$index;
121 | }
122 | }
123 |
124 | /**
125 | * @return array
126 | */
127 | public function readChunkOfTheContentDataProvider()
128 | {
129 | $content = $this->contentAsArray;
130 | $indices = array_keys($content);
131 | $length = count($indices);
132 |
133 | return [
134 | 'read only the first line' => [
135 | 'content' => $content,
136 | 'end' => $indices[1],
137 | 'start' => $indices[0]
138 | ],
139 | 'read one line the middle' => [
140 | 'content' => $content,
141 | 'end' => $indices[2],
142 | 'start' => $indices[1]
143 | ],
144 | 'read whole content' => [
145 | 'content' => $content,
146 | 'end' => $indices[($length - 1)],
147 | 'start' => $indices[0]
148 | ]
149 | ];
150 | }
151 |
152 | /**
153 | * @dataProvider readChunkOfTheContentDataProvider
154 | * @param array $content
155 | * @param int $end
156 | * @param int $start
157 | */
158 | public function testReadChunkOfTheContentByProvidingStartLineNumberAndAmountOfLines(array $content, $end, $start)
159 | {
160 | $file = $this->createFile();
161 | $filesystem = $this->createFilesystem();
162 | $length = ($end - $start);
163 | $reader = $this->createReader();
164 |
165 | $file->setContent($this->convertArrayToStrings($content));
166 | $filesystem->addChild($file);
167 | $reader->setPath($file->url());
168 |
169 | $expectedContent = [];
170 |
171 | $counter = $start;
172 |
173 | while ($counter < $end) {
174 | $expectedContent[] = $content[$counter];
175 | ++$counter;
176 | }
177 |
178 | $this->assertEquals($expectedContent, $reader->readMany($length, $start));
179 | }
180 |
181 | public function testReadContentByProvidingTheCurrentLineNumber()
182 | {
183 | $data = $this->contentAsArray;
184 | $file = $this->createFile();
185 | $filesystem = $this->createFilesystem();
186 | $reader = $this->createReader();
187 |
188 | $file->setContent($this->getContentAsString());
189 | $filesystem->addChild($file);
190 | $reader->setPath($file->url());
191 |
192 | foreach ($data as $lineNumber => $line) {
193 | $this->assertEquals($line, $reader->readOne($lineNumber));
194 | }
195 | }
196 |
197 | public function testReadContentByProvidingTheCurrentLineNumberByUsingReaderAsAFunction()
198 | {
199 | $data = $this->contentAsArray;
200 | $file = $this->createFile();
201 | $filesystem = $this->createFilesystem();
202 | $reader = $this->createReader();
203 | $file->setContent($this->getContentAsString());
204 | $filesystem->addChild($file);
205 | $reader->setPath($file->url());
206 | foreach($data as $lineNumber => $line) {
207 | $this->assertEquals($line, $reader($lineNumber));
208 | }
209 | }
210 |
211 | /**
212 | * @return string
213 | */
214 | protected function getContentAsString()
215 | {
216 | return $this->convertArrayToStrings($this->contentAsArray);
217 | }
218 |
219 | /**
220 | * @param array $data
221 | * @param string $delimiter
222 | * @return string
223 | */
224 | protected function convertArrayToStrings(array $data, $delimiter = ',')
225 | {
226 | $string = '';
227 |
228 | foreach ($data as $contents) {
229 | $string .= implode($delimiter, $contents) . PHP_EOL;
230 | }
231 |
232 | return $string;
233 | }
234 |
235 | /**
236 | * @param array $headline
237 | * @param array $content
238 | * @return array
239 | */
240 | private function addHeadlineAsKeysToContent(array $headline, array $content)
241 | {
242 | $adaptedContent = [];
243 |
244 | foreach ($content as $key => $columns) {
245 | $adaptedContent[$key] = [];
246 | foreach ($columns as $columnKey => $columnContent) {
247 | $adaptedContent[$key][$headline[$columnKey]] = $columnContent;
248 | }
249 | }
250 |
251 | return $adaptedContent;
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/test/WriterTest.php:
--------------------------------------------------------------------------------
1 |
4 | * @since 2015-05-03
5 | */
6 |
7 | namespace Test\Net\Bazzline\Component\Csv;
8 |
9 | //@todo implement call of this tests with different delimiters etc. (after the
10 | //setters are developed
11 | //@todo implement writeOne(!array)
12 | class WriterTest extends AbstractTestCase
13 | {
14 | /**
15 | * @var array
16 | */
17 | protected $contentAsArray = [
18 | [
19 | 'headlines foo',
20 | 'headlines bar'
21 | ],
22 | [
23 | 'foo',
24 | 'bar'
25 | ],
26 | [
27 | 'foobar',
28 | 'baz'
29 | ],
30 | [
31 | 'baz',
32 | 'barfoo'
33 | ]
34 | ];
35 |
36 | protected $enclosures = [
37 | 's',
38 | '"',
39 | '|'
40 | ];
41 |
42 | protected $delimiters = [
43 | 's',
44 | 'l',
45 | '-',
46 | ',',
47 | ';'
48 | ];
49 |
50 | public function testWriteContentLinePerLineUsingWriteOne()
51 | {
52 | $delimiters = $this->delimiters;
53 |
54 | foreach ($delimiters as $delimiter) {
55 | $collection = $this->contentAsArray;
56 | $expectedContent = $this->convertArrayToStrings($collection, $delimiter);
57 | $file = $this->createFile();
58 | $filesystem = $this->createFilesystem();
59 | $writer = $this->createWriter();
60 |
61 | $filesystem->addChild($file);
62 | $writer->setDelimiter($delimiter);
63 | $writer->setPath($file->url());
64 |
65 | foreach ($collection as $content) {
66 | $this->assertNotFalse($writer->writeOne($content));
67 | }
68 |
69 | $this->assertEquals($expectedContent, $file->getContent());
70 | }
71 | }
72 |
73 | public function testWriteContentLinePerLineUsingWriterAsAFunction()
74 | {
75 | $delimiters = $this->delimiters;
76 |
77 | foreach ($delimiters as $delimiter) {
78 | $collection = $this->contentAsArray;
79 | $expectedContent = $this->convertArrayToStrings($collection, $delimiter);
80 | $file = $this->createFile();
81 | $filesystem = $this->createFilesystem();
82 | $writer = $this->createWriter();
83 |
84 | $filesystem->addChild($file);
85 | $writer->setDelimiter($delimiter);
86 | $writer->setPath($file->url());
87 |
88 | foreach ($collection as $content) {
89 | $this->assertNotFalse($writer($content));
90 | }
91 |
92 | $this->assertEquals($expectedContent, $file->getContent());
93 | }
94 | }
95 |
96 | public function testWriteAllContentAtOnce()
97 | {
98 | $delimiters = $this->delimiters;
99 |
100 | foreach ($delimiters as $delimiter) {
101 | $collection = $this->contentAsArray;
102 | $expectedContent = $this->convertArrayToStrings($collection, $delimiter);
103 | $file = $this->createFile();
104 | $filesystem = $this->createFilesystem();
105 | $writer = $this->createWriter();
106 |
107 | $filesystem->addChild($file);
108 | $writer->setDelimiter($delimiter);
109 | $writer->setPath($file->url());
110 |
111 | //simple write the content two times
112 | $this->assertNotFalse($writer->writeMany($collection));
113 | $this->assertNotFalse($writer->writeAll($collection));
114 |
115 | $this->assertEquals($expectedContent, $file->getContent());
116 | }
117 | }
118 |
119 | public function testWriteManyContentAtOnce()
120 | {
121 | $delimiters = $this->delimiters;
122 |
123 | foreach ($delimiters as $delimiter) {
124 | $collection = $this->contentAsArray;
125 | $expectedContent = $this->convertArrayToStrings($collection, $delimiter);
126 | $file = $this->createFile();
127 | $filesystem = $this->createFilesystem();
128 | $writer = $this->createWriter();
129 |
130 | $filesystem->addChild($file);
131 | $writer->setDelimiter($delimiter);
132 | $writer->setPath($file->url());
133 |
134 | $this->assertNotFalse($writer->writeMany($collection));
135 |
136 | $this->assertEquals($expectedContent, $file->getContent());
137 | }
138 | }
139 |
140 | public function testTruncate()
141 | {
142 | $delimiters = $this->delimiters;
143 |
144 | foreach ($delimiters as $delimiter) {
145 | $collection = $this->contentAsArray;
146 | $content = $this->convertArrayToStrings($collection, $delimiter);
147 | $file = $this->createFile();
148 | $filesystem = $this->createFilesystem();
149 | $writer = $this->createWriter();
150 |
151 | $file->setContent($content);
152 | $filesystem->addChild($file);
153 | $writer->setDelimiter($delimiter);
154 | $writer->setPath($file->url());
155 |
156 | $writer->truncate();
157 |
158 | $this->assertEquals('', $file->getContent());
159 | }
160 | }
161 |
162 | public function testDelete()
163 | {
164 | $delimiters = $this->delimiters;
165 |
166 | foreach ($delimiters as $delimiter) {
167 | $collection = $this->contentAsArray;
168 | $content = $this->convertArrayToStrings($collection, $delimiter);
169 | $file = $this->createFile();
170 | $filesystem = $this->createFilesystem();
171 | $writer = $this->createWriter();
172 |
173 | $file->setContent($content);
174 | $filesystem->addChild($file);
175 | $writer->setDelimiter($delimiter);
176 | $writer->setPath($file->url());
177 |
178 | $this->assertTrue(file_exists($file->url()));
179 | $writer->delete();
180 | $this->assertFalse(file_exists($file->url()));
181 | }
182 | }
183 |
184 | public function testCopy()
185 | {
186 | $delimiters = $this->delimiters;
187 |
188 | foreach ($delimiters as $delimiter) {
189 | $collection = $this->contentAsArray;
190 | $content = $this->convertArrayToStrings($collection, $delimiter);
191 | $file = $this->createFile();
192 | $filesystem = $this->createFilesystem();
193 | $writer = $this->createWriter();
194 |
195 | $file->setContent($content);
196 | $filesystem->addChild($file);
197 |
198 | $sourceFilePath = $file->url();
199 | $destinationFilePath = str_replace('test.csv', 'foobar.csv', $file->url());
200 |
201 | $writer->setDelimiter($delimiter);
202 | $writer->setPath($sourceFilePath);
203 |
204 | $this->assertFalse(file_exists($destinationFilePath));
205 | $writer->copy($destinationFilePath);
206 | $this->assertTrue(file_exists($destinationFilePath));
207 | }
208 | }
209 |
210 | public function testCopyWithSettingNewPathAsCurrentPath()
211 | {
212 | $delimiters = $this->delimiters;
213 |
214 | foreach ($delimiters as $delimiter) {
215 | $collection = $this->contentAsArray;
216 | $content = $this->convertArrayToStrings($collection, $delimiter);
217 | $file = $this->createFile();
218 | $filesystem = $this->createFilesystem();
219 | $writer = $this->createWriter();
220 |
221 | $file->setContent($content);
222 | $filesystem->addChild($file);
223 |
224 | $sourceFilePath = $file->url();
225 | $destinationFilePath = str_replace('test.csv', 'foobar.csv', $file->url());
226 |
227 | $writer->setDelimiter($delimiter);
228 | $writer->setPath($sourceFilePath);
229 |
230 | $this->assertFalse(file_exists($destinationFilePath));
231 | $writer->copy($destinationFilePath, true);
232 | $this->assertTrue(file_exists($destinationFilePath));
233 | }
234 | }
235 |
236 | public function testMove()
237 | {
238 | $delimiters = $this->delimiters;
239 |
240 | foreach ($delimiters as $delimiter) {
241 | $collection = $this->contentAsArray;
242 | $content = $this->convertArrayToStrings($collection, $delimiter);
243 | $file = $this->createFile();
244 | $filesystem = $this->createFilesystem();
245 | $writer = $this->createWriter();
246 |
247 | $file->setContent($content);
248 | $filesystem->addChild($file);
249 |
250 | $sourceFilePath = $file->url();
251 | $destinationFilePath = str_replace('test.csv', 'foobar.csv', $file->url());
252 |
253 | $writer->setDelimiter($delimiter);
254 | $writer->setPath($sourceFilePath);
255 |
256 | $this->assertFalse(file_exists($destinationFilePath));
257 | $writer->move($destinationFilePath);
258 | $this->assertTrue(file_exists($destinationFilePath));
259 | }
260 | }
261 |
262 | /**
263 | * @param array $data
264 | * @param string $delimiter
265 | * @return string
266 | */
267 | protected function convertArrayToStrings(array $data, $delimiter = ',')
268 | {
269 | $string = '';
270 |
271 | foreach ($data as $contents) {
272 | foreach ($contents as &$part) {
273 | $contains = $this->stringContains(' ');
274 | if ($contains->evaluate($part, '', true)) {
275 | $part = '"' . $part . '"';
276 | }
277 | }
278 | $string .= implode($delimiter, $contents) . PHP_EOL;
279 | }
280 |
281 | return $string;
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/test/bootstrap.php:
--------------------------------------------------------------------------------
1 |