63 Commits
1.2.1 ... main

Author SHA1 Message Date
Christian Bläul
6c0e74c89e Newer Rector versions don’t have AddCoversClassAttributeRector anymore
https://github.com/rectorphp/rector-phpunit/pull/561/
2026-02-02 16:02:11 +01:00
Christian Bläul
c47ba7934c Let’s see if we can update the GH Action to cache@v4 2026-02-02 16:02:11 +01:00
Christian Bläul
653d5c0307 Upgrade to PHPUnit 8.5.52 2026-02-02 16:02:11 +01:00
Christian Bläul
ff0e0c77fe PHPUnit test was not compatible with PHP ^7.0 2024-08-29 17:33:01 +02:00
Christian Bläul
f9b08cb0ac Re-add support for PHPUnit 6.5.14 and PHP ^7.0 2024-08-29 17:30:33 +02:00
Christian Bläul
71e5f223a4 Make PHP 8.3 support visible in README 2024-08-29 17:20:31 +02:00
Christian Bläul
24e229ddfa Convert some old array() calls to new [] syntax 2024-08-29 17:20:31 +02:00
Christian Bläul
259e9a6c31 Extend test coverage to PHP 8.3 2024-08-29 17:20:31 +02:00
Christian Bläul
27ab8a3e05 README.md: Remove OpenCollective as no funds are needed nor given 2024-07-29 21:53:36 +02:00
Fonata
fe27ca6a7d docs: add badge for PHPUnit 2022-12-27 16:39:03 +01:00
Daniele Scasciafratte
33f0b6aa7c Support to search for numbers 2022-12-27 16:31:34 +01:00
Fonata
260de6126c test: make code compatible with PHP 8 2022-12-26 23:45:55 +01:00
Fonata
1465973860 ci: make actions use node 16 instead of node 12 2022-12-26 23:45:55 +01:00
Fonata
2c46b3fd14 test: make code compatible with more recent PHPUnit versions 2022-12-26 23:45:55 +01:00
Fonata
bb3abe97de ci: update phpunit if allowed by the PHP version 2022-12-26 23:45:55 +01:00
Fonata
ac6b2665cd ci: tell GitHub to also run tests for PHP 8.* 2022-12-26 23:45:55 +01:00
Fonata
919528bb2f test: fix expected string: '\r' were 2 characters, not 0x0d
Solved with var_dump(bin2hex($expected));
2022-12-26 20:29:04 +01:00
Fonata
c4a90941e2 docs: fix grammar mistake 2022-12-26 20:29:04 +01:00
Fonata
e967067a46 refactor: simplify code 2022-12-26 20:29:04 +01:00
Fonata
11dbbcb40e Simplified code - the string content is exactly the same 2022-12-26 20:29:04 +01:00
Fonata
67163568ee Improved test name to make it easier to find 2022-12-26 20:29:04 +01:00
Fonata
6648dc99d5 Added a test for issue #177: Saving data with commas 2022-12-26 20:29:04 +01:00
Fonata
2d6236cae0 docs(changelog): add version 1.3.2 2021-11-07 15:15:46 +01:00
Fonata
f2b0aecd12 docs: remove Travis from README; make running act locally easier 2021-11-07 13:36:38 +01:00
Fonata
5d4643b201 Allow _guess_delimiter to work with a single row of data
Fix #206
2021-11-07 13:36:38 +01:00
d5606f8b2a ci(travis): remove .travis.yml config file
If we're switching to GitHub Actions, there's no need to keep the
Travis-CI config file around.
2021-11-07 13:36:38 +01:00
8cebcbd9bb ci(github): add GitHub Actions CI workflow
Can be tested locally via the local-ci make target, which requires
Docker and act (https://github.com/nektos/act).
2021-11-07 13:36:38 +01:00
518f5081fb chore: improve editorconfig for YAML and Makefile 2021-11-07 13:36:38 +01:00
Fonata
a28fc6ab0a refactor: simplify code without changing behavior 2021-11-07 13:36:38 +01:00
Fonata
009820d190 Explicit email address for security problems 2021-06-20 23:25:35 +02:00
Fonata
facdf1c06c Set release date 2021-06-20 23:25:26 +02:00
Fonata
ee13c17157 Apply PhpStorm source code formatting 2021-06-20 23:21:46 +02:00
Fonata
99daaa7235 Bugfix: $csv->parseFile now sets $this->data
This adds consistency because $csv->parse() does the same.

Fix #200
Fix #201
2021-06-20 23:21:46 +02:00
Fonata
05826c2bbf Make return value of parseFile() explicit as false if parsing failed
In practise this changes nothing because $this->file_data would typically be empty.
The only exception is if the object was reused from a previous parsing operation.
2021-06-20 23:21:46 +02:00
Fonata
731900effe DocBlock types: Use Psalm notation for 2-dimensional array 2021-06-20 23:21:46 +02:00
Fonata
913c3b1b94 Source code comments: add more information to DocBlocks 2021-06-20 23:21:46 +02:00
Fonata
96b2784d3c Source code comments: don’t repeat the field we are describing
The motivation for this change is how the PhpStorm IDE displays help texts.
Because the enter is shown as a space in the tooltips/help hovers, the
previous version was harder to read than after this commit.
2021-06-20 23:21:46 +02:00
Fonata
be01bc9ae4 README.md: Fix incorrect constructor usage 2021-05-03 21:26:56 +02:00
Fonata
b444afae2f Specify release date 2021-04-14 20:14:01 +02:00
Fonata
bd6befe01e Remove low-value comment 2021-04-14 20:12:43 +02:00
Fonata
05ed8ec667 Rename parameter to reduce confusion 2021-04-14 20:12:43 +02:00
Fonata
d21233c0f8 Remove Travis tests for PHP 7.0
It said:
> TypeError: Return value of ParseCsv\tests\properties\BaseClass::setUp() must be an instance of void, none returned
2021-04-14 20:12:43 +02:00
Fonata
7a066f9d5e Add test for empty first parameter 2021-04-14 20:12:43 +02:00
Fonata
2425c1bbda Deprecation: Stronger language in PHP DocBlock 2021-04-14 20:12:43 +02:00
Fonata
eb502f9dad PHP DocBlock: use more common type name 2021-04-14 20:12:43 +02:00
Fonata
0af6e98670 Tests should not produce output - capture it 2021-04-14 20:12:43 +02:00
Fonata
b38d0de800 Add test for getTotalDataRowCount without loading data 2021-04-14 20:12:43 +02:00
Fonata
e15d144e27 Adjust call to static function 2021-04-14 20:12:43 +02:00
Fonata
4518e944c4 Adjust function signatures for newer PHPUnit versions
Will make an update to PHPUnit 8 or 9 easier.
2021-04-14 20:12:43 +02:00
Fonata
3ce6a82052 Make deprecation clearer 2021-04-14 20:12:43 +02:00
Fonata
abae91a1a2 Make condition easier to read; no change in logic 2021-04-14 20:12:43 +02:00
Fonata
45f079b5dd Code comment for future me: I was afraid the code would surprise users 2021-04-14 20:12:43 +02:00
Fonata
fba67b84ff More consistent formatting 2021-04-14 20:12:43 +02:00
Fonata
2a154869b4 Fix PHP DocBlock: some tools thought the function always returns true
(Also, there was a spelling mistake)
2021-04-14 20:12:43 +02:00
Fonata
d1186fcafa Deprecate support for file paths in __construct() and parse()
See https://github.com/parsecsv/parsecsv-for-php/issues/198
2021-04-14 20:12:43 +02:00
Fonata
9c5eee23f6 Fix examples in README.md: add missing semicolons 2021-04-14 20:12:43 +02:00
Fonata
6a54a6a509 Move parsing of .php files (sic!) to _rfile for easier refactoring
Note that this arcane code comes from v0.2.0 beta:
5f0e6b03d4
2021-04-14 20:12:43 +02:00
Fonata
8277b9682d Ignore files on the root directory only 2021-04-14 20:12:43 +02:00
Fonata
0e54a2b9fa Make testing easier 2021-04-14 20:12:43 +02:00
Fonata
9c35237b1b README: make sure examples can be used in namespaced files 2020-12-15 16:20:14 +01:00
Fonata
1528a1f6ae README.md: fix the function name fix from my last commit 2020-12-07 18:13:09 +01:00
Fonata
aadb654edf Fix incorrect function name in README.md
Closes #195
2020-11-12 06:40:58 +01:00
Sergei Morozov
8069409c73 Add .gitattributes 2020-05-10 14:57:29 +02:00
29 changed files with 686 additions and 369 deletions

View File

@@ -14,5 +14,8 @@ insert_final_newline = true
[composer.json] [composer.json]
indent_size = 4 indent_size = 4
[.travis.yml] [Makefile]
indent_style = tab
[*.yml,*.yaml]
indent_size = 2 indent_size = 2

9
.gitattributes vendored Normal file
View File

@@ -0,0 +1,9 @@
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/ChangeLog.txt export-ignore
/examples export-ignore
/Makefile export-ignore
/ruleset.xml export-ignore
/tests export-ignore

82
.github/workflows/phpunit.yml vendored Normal file
View File

@@ -0,0 +1,82 @@
---
name: PHPUnit
on:
push:
jobs:
test_php_82_and_newer:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php_version:
- "8.3"
- "8.2"
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_version }}
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Update PHPUnit
run: composer require phpunit/phpunit --dev -W
- name: Install dependencies
run: composer update
- name: Validate dependencies
run: composer validate
- name: install Rector
run: composer require rector/rector --dev -W
- name: run Rector
run: cd tests && ../vendor/bin/phpunit --migrate-configuration
shell: bash
- name: run Rector
run: cd tests && ../vendor/bin/rector process .
shell: bash
- name: Run tests
run: vendor/bin/phpunit --configuration tests/phpunit.xml
test_php_81_and_lower:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php_version:
- "8.1"
- "8.0"
- "7.4"
- "7.3"
- "7.2"
- "7.1"
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php_version }}
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Update PHPUnit
run: composer require phpunit/phpunit --dev -W
- name: Install dependencies
run: composer update
- name: Validate dependencies
run: composer validate
- name: Run tests
run: vendor/bin/phpunit --configuration tests/phpunit.xml

12
.gitignore vendored
View File

@@ -1,6 +1,14 @@
*.bak *.bak
.env
.envrc
/.idea /.idea
/.vscode
/composer-setup.php
/composer.lock
/coverage_clover.xml
/docker-compose.yml
/examples/people.csv
/phive.xml /phive.xml
/tests/.phpunit.result.cache
/tools /tools
composer.lock /vendor/
vendor/

View File

@@ -1,25 +0,0 @@
dist: trusty
language: php
dist: trusty
php:
- '7.4'
- '7.3'
- '7.2'
- '7.1'
- '7.0'
before_install:
- composer update
script:
- composer validate
- vendor/bin/phpunit --version
- vendor/bin/phpunit --configuration tests/phpunit.xml
notifications:
email:
recipients:
- will.knauss@gmail.com
on_success: never
on_failure: always

View File

@@ -1,3 +1,67 @@
ParseCSV 1.3.2
-----------------------------------
Date: 07-Nov-2021
Bugfix:
- Allow _guess_delimiter to work with a single
row of data. As a consequence, `$csv->auto()`
now works for files with just one row of data.
See issue #206.
-----------------------------------
ParseCSV 1.3.1
-----------------------------------
Date: 20-Jun-2021
Bugfix:
- `parseFile()` will now set `$csv->data`.
Until now, the parsed data was only returned.
This adds consistency with `$csv->parse()`
for the following operations on the object.
-----------------------------------
ParseCSV 1.3.0
-----------------------------------
Date: 14-Apr-2021
Breaking changes:
- Passing file paths to parse() or new Csv() is now deprecated
and will be removed in v2.0.0. Use ->parseFile() instead.
WARNING: It will call trigger_error() for now.
This may still break your application, depending on your
php.ini config. You can add E_USER_DEPRECATED to the
error_reporting mask to avoid breaking your application:
Example:
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_USER_DEPRECATED
This change is to avoid security issues: see issue #198.
Non-breaking deprecations:
- The function load_data() is deprecated.
Call loadFile() or loadDataString() instead.
- Supplying CSV data (file content) to auto() is deprecated.
Please use autoDetectionForDataString().
The motivation is to make subtle gotchas less likely: when the
file cannot be found or read by PHP (permissions), its file path
would be treated like CSV data.
- Currently, there is code to parse .php: <?...?> tags are removed
before the remaining file content is treated like a .csv file.
This rarely-used functionality will be removed in v2.0.0.
You are only affected if you use ParseCsv to parse .php files.
New features: none
Bug fixes: none
-----------------------------------
ParseCSV 1.2.1 ParseCSV 1.2.1
----------------------------------- -----------------------------------
Date: 25-Apr-2020 Date: 25-Apr-2020

View File

@@ -11,5 +11,21 @@ phpunit-dep:
exit 1 \ exit 1 \
) )
# Requires:
# - Docker: https://docker.com
# - act: https://github.com/nektos/act
local-ci:
ifeq (, $(shell which act))
define ACT_ERROR
Consider running the following to install 'act':
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
The dependency 'act' was not found
endef
$(error ${ACT_ERROR})
endif
act -P ubuntu-latest=shivammathur/node:latest -W .github/workflows/ci.yml
.SILENT: .SILENT:
.PHONY: test phpunit-dep .PHONY: test phpunit-dep local-ci

111
README.md
View File

@@ -1,12 +1,12 @@
# ParseCsv # ParseCsv
[![Financial Contributors on Open Collective](https://opencollective.com/parsecsv/all/badge.svg?label=financial+contributors)](https://opencollective.com/parsecsv) ![PHPUnit](https://github.com/parsecsv/parsecsv-for-php/actions/workflows/phpunit.yml/badge.svg)
ParseCsv is an easy-to-use PHP class that reads and writes CSV data properly. It ParseCsv is an easy-to-use PHP class that reads and writes CSV data properly. It
fully conforms to the specifications outlined on the on the fully conforms to the specifications outlined on the
[Wikipedia article][CSV] (and thus RFC 4180). It has many advanced features which help make your [Wikipedia article][CSV] (and thus RFC 4180). It has many advanced features which help make your
life easier when dealing with CSV data. life easier when dealing with CSV data.
You may not need a library at all: before using ParseCsv, please make sure if PHP's own `str_getcsv()`, ``fgetcvs()`` or `fputcsv()` meets your needs. You may not need a library at all: before using ParseCsv, please make sure if PHP's own `str_getcsv()`, ``fgetcsv()`` or `fputcsv()` meets your needs.
This library was originally created in early 2007 by [jimeh](https://github.com/jimeh) due to the lack of built-in This library was originally created in early 2007 by [jimeh](https://github.com/jimeh) due to the lack of built-in
and third-party support for handling CSV data in PHP. and third-party support for handling CSV data in PHP.
@@ -28,7 +28,7 @@ and third-party support for handling CSV data in PHP.
* Support for character encoding conversion using PHP's * Support for character encoding conversion using PHP's
`iconv()` and `mb_convert_encoding()` functions. `iconv()` and `mb_convert_encoding()` functions.
* Supports PHP 5.5 and higher. * Supports PHP 5.5 and higher.
It certainly works with PHP 7.2 and all versions in between. It certainly works with PHP 8.3 and all versions in between.
## Installation ## Installation
@@ -53,27 +53,20 @@ To use ParseCSV, you then have to add a `require 'parsecsv.lib.php';` line.
## Example Usage ## Example Usage
**General** **Parse a tab-delimited CSV file with encoding conversion**
```php ```php
$csv = new ParseCsv\Csv('data.csv'); $csv = new \ParseCsv\Csv();
print_r($csv->data);
```
**Tab delimited, and encoding conversion**
```php
$csv = new ParseCsv\Csv();
$csv->encoding('UTF-16', 'UTF-8'); $csv->encoding('UTF-16', 'UTF-8');
$csv->delimiter = "\t"; $csv->delimiter = "\t";
$csv->parse('data.tsv'); $csv->parseFile('data.tsv');
print_r($csv->data); print_r($csv->data);
``` ```
**Auto-detect delimiter character** **Auto-detect field delimiter character**
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->auto('data.csv'); $csv->auto('data.csv');
print_r($csv->data); print_r($csv->data);
``` ```
@@ -81,25 +74,25 @@ print_r($csv->data);
**Parse data with offset** **Parse data with offset**
* ignoring the first X (e.g. two) rows * ignoring the first X (e.g. two) rows
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->offset = 2; $csv->offset = 2;
$csv->parse('data.csv'); $csv->parseFile('data.csv');
print_r($csv->data); print_r($csv->data);
``` ```
**Limit the number of returned data rows** **Limit the number of returned data rows**
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->limit = 5; $csv->limit = 5;
$csv->parse('data.csv'); $csv->parseFile('data.csv');
print_r($csv->data); print_r($csv->data);
``` ```
**Get total number of data rows without parsing whole data** **Get total number of data rows without parsing whole data**
* Excluding heading line if present (see $csv->header property) * Excluding heading line if present (see $csv->header property)
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->load_data('data.csv'); $csv->loadFile('data.csv');
$count = $csv->getTotalDataRowCount(); $count = $csv->getTotalDataRowCount();
print_r($count); print_r($count);
``` ```
@@ -107,8 +100,8 @@ print_r($count);
**Get most common data type for each column** **Get most common data type for each column**
```php ```php
$csv = new ParseCsv\Csv('data.csv'); $csv = new \ParseCsv\Csv('data.csv');
$csv->getDatatypes() $csv->getDatatypes();
print_r($csv->data_types); print_r($csv->data_types);
``` ```
@@ -116,9 +109,9 @@ print_r($csv->data_types);
Change data values: Change data values:
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->sort_by = 'id'; $csv->sort_by = 'id';
$csv->parse('data.csv'); $csv->parseFile('data.csv');
# "4" is the value of the "id" column of the CSV row # "4" is the value of the "id" column of the CSV row
$csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com'); $csv->data[4] = array('firstname' => 'John', 'lastname' => 'Doe', 'email' => 'john@doe.com');
$csv->save(); $csv->save();
@@ -126,8 +119,8 @@ $csv->save();
Enclose each data value by quotes: Enclose each data value by quotes:
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->parse('data.csv'); $csv->parseFile('data.csv');
$csv->enclose_all = true; $csv->enclose_all = true;
$csv->save(); $csv->save();
``` ```
@@ -135,9 +128,9 @@ $csv->save();
**Replace field names or set ones if missing** **Replace field names or set ones if missing**
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->fields = ['id', 'name', 'category'] $csv->fields = ['id', 'name', 'category'];
$csv->parse('data.csv'); $csv->parseFile('data.csv');
``` ```
**Add row/entry to end of CSV file** **Add row/entry to end of CSV file**
@@ -145,20 +138,46 @@ $csv->parse('data.csv');
_Only recommended when you know the exact structure of the file._ _Only recommended when you know the exact structure of the file._
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->save('data.csv', array(array('1986', 'Home', 'Nowhere', '')), /* append */ true); $csv->save('data.csv', array(array('1986', 'Home', 'Nowhere', '')), /* append */ true);
``` ```
**Convert 2D array to CSV data and send headers to browser to treat output as **Convert 2D array to CSV data and send headers to browser to treat output as
a file and download it** a file and download it**
Your web app users would call this an export.
```php ```php
$csv = new ParseCsv\Csv(); $csv = new \ParseCsv\Csv();
$csv->output('movies.csv', $array, array('field 1', 'field 2'), ','); $csv->linefeed = "\n";
$header = array('field 1', 'field 2');
$csv->output('movies.csv', $data_array, $header, ',');
``` ```
For more complex examples, see the ``tests`` and `examples` directories. For more complex examples, see the ``tests`` and `examples` directories.
## Test coverage
All tests are located in the `tests` directory. To execute tests, run the following commands:
````bash
composer install
composer run test
````
Note that PHP 8.2 and newer allow PHPUnit versions that deprecate `@annotations`. The GitHub actions use Rector to
convert them to `#[attributes]`.
When pushing code to GitHub, tests will be executed using GitHub Actions. The relevant configuration is in the
file `.github/workflows/ci.yml`. To run the `test` action locally, you can execute the following command:
````bash
make local-ci
````
## Security
If you discover any security related issues, please email ParseCsv@blaeul.de instead of using GitHub issues.
## Credits ## Credits
* ParseCsv is based on the concept of [Ming Hong Ng][ming]'s [CsvFileParser][] * ParseCsv is based on the concept of [Ming Hong Ng][ming]'s [CsvFileParser][]
@@ -177,30 +196,6 @@ This project exists thanks to all the people who contribute.
Please find a complete list on the project's [contributors][] page. Please find a complete list on the project's [contributors][] page.
[contributors]: https://github.com/parsecsv/parsecsv-for-php/graphs/contributors [contributors]: https://github.com/parsecsv/parsecsv-for-php/graphs/contributors
<a href="https://github.com/parsecsv/parsecsv-for-php/graphs/contributors"><img src="https://opencollective.com/parsecsv/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/parsecsv/contribute)]
#### Individuals
<a href="https://opencollective.com/parsecsv"><img src="https://opencollective.com/parsecsv/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/parsecsv/contribute)]
<a href="https://opencollective.com/parsecsv/organization/0/website"><img src="https://opencollective.com/parsecsv/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/1/website"><img src="https://opencollective.com/parsecsv/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/2/website"><img src="https://opencollective.com/parsecsv/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/3/website"><img src="https://opencollective.com/parsecsv/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/4/website"><img src="https://opencollective.com/parsecsv/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/5/website"><img src="https://opencollective.com/parsecsv/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/6/website"><img src="https://opencollective.com/parsecsv/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/7/website"><img src="https://opencollective.com/parsecsv/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/8/website"><img src="https://opencollective.com/parsecsv/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/parsecsv/organization/9/website"><img src="https://opencollective.com/parsecsv/organization/9/avatar.svg"></a>
## License ## License

View File

@@ -34,7 +34,7 @@
"php": ">=5.5" "php": ">=5.5"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6", "phpunit/phpunit": "8.5.52",
"squizlabs/php_codesniffer": "^3.5" "squizlabs/php_codesniffer": "^3.5"
}, },
"suggest": { "suggest": {
@@ -45,6 +45,11 @@
"dev-master": "1.0.x-dev" "dev-master": "1.0.x-dev"
} }
}, },
"scripts": {
"test": [
"vendor/bin/phpunit -c tests tests --disallow-test-output --coverage-clover coverage_clover.xml --whitelist src"
]
},
"support": { "support": {
"issues": "https://github.com/parsecsv/parsecsv-for-php/issues", "issues": "https://github.com/parsecsv/parsecsv-for-php/issues",
"source": "https://github.com/parsecsv/parsecsv-for-php" "source": "https://github.com/parsecsv/parsecsv-for-php"

View File

@@ -18,8 +18,8 @@ $csv->auto('_books.csv');
# if its not the default comma... # if its not the default comma...
// $csv->delimiter = "\t"; # tab delimited // $csv->delimiter = "\t"; # tab delimited
# ...and then use the parse() function. # ...and then use the parseFile() function.
// $csv->parse('_books.csv'); // $csv->parseFile('_books.csv');
# Output result. # Output result.

View File

@@ -49,7 +49,7 @@ class Csv {
*/ */
/** /**
* Heading * Header row:
* Use first line/entry as field names * Use first line/entry as field names
* *
* @var bool * @var bool
@@ -57,7 +57,6 @@ class Csv {
public $heading = true; public $heading = true;
/** /**
* Fields
* Override field names * Override field names
* *
* @var array * @var array
@@ -65,7 +64,6 @@ class Csv {
public $fields = array(); public $fields = array();
/** /**
* Sort By
* Sort CSV by this field * Sort CSV by this field
* *
* @var string|null * @var string|null
@@ -73,15 +71,13 @@ class Csv {
public $sort_by = null; public $sort_by = null;
/** /**
* Sort Reverse * Reverse the sort direction
* Reverse the sort function
* *
* @var bool * @var bool
*/ */
public $sort_reverse = false; public $sort_reverse = false;
/** /**
* Sort Type
* Sort behavior passed to sort methods * Sort behavior passed to sort methods
* *
* regular = SORT_REGULAR * regular = SORT_REGULAR
@@ -93,31 +89,34 @@ class Csv {
public $sort_type = SortEnum::SORT_TYPE_REGULAR; public $sort_type = SortEnum::SORT_TYPE_REGULAR;
/** /**
* Delimiter * Field delimiter character
* Delimiter character
* *
* @var string * @var string
*/ */
public $delimiter = ','; public $delimiter = ',';
/** /**
* Enclosure
* Enclosure character * Enclosure character
* *
* This is useful for cell values that are either multi-line
* or contain the field delimiter character.
*
* @var string * @var string
*/ */
public $enclosure = '"'; public $enclosure = '"';
/** /**
* Enclose All * Force enclosing all columns.
* Force enclosing all columns *
* If false, only cells that are either multi-line or
* contain the field delimiter character are enclosed
* in the $enclosure char.
* *
* @var bool * @var bool
*/ */
public $enclose_all = false; public $enclose_all = false;
/** /**
* Conditions
* Basic SQL-Like conditions for row matching * Basic SQL-Like conditions for row matching
* *
* @var string|null * @var string|null
@@ -125,7 +124,6 @@ class Csv {
public $conditions = null; public $conditions = null;
/** /**
* Offset
* Number of rows to ignore from beginning of data. If present, the heading * Number of rows to ignore from beginning of data. If present, the heading
* row is also counted (if $this->heading == true). In other words, * row is also counted (if $this->heading == true). In other words,
* $offset == 1 and $offset == 0 have the same meaning in that situation. * $offset == 1 and $offset == 0 have the same meaning in that situation.
@@ -135,7 +133,6 @@ class Csv {
public $offset = null; public $offset = null;
/** /**
* Limit
* Limits the number of returned rows to the specified amount * Limits the number of returned rows to the specified amount
* *
* @var int|null * @var int|null
@@ -143,7 +140,6 @@ class Csv {
public $limit = null; public $limit = null;
/** /**
* Auto Depth
* Number of rows to analyze when attempting to auto-detect delimiter * Number of rows to analyze when attempting to auto-detect delimiter
* *
* @var int * @var int
@@ -151,7 +147,6 @@ class Csv {
public $auto_depth = 15; public $auto_depth = 15;
/** /**
* Auto Non Chars
* Characters that should be ignored when attempting to auto-detect delimiter * Characters that should be ignored when attempting to auto-detect delimiter
* *
* @var string * @var string
@@ -159,7 +154,6 @@ class Csv {
public $auto_non_chars = "a-zA-Z0-9\n\r"; public $auto_non_chars = "a-zA-Z0-9\n\r";
/** /**
* Auto Preferred
* preferred delimiter characters, only used when all filtering method * preferred delimiter characters, only used when all filtering method
* returns multiple possible delimiters (happens very rarely) * returns multiple possible delimiters (happens very rarely)
* *
@@ -168,15 +162,14 @@ class Csv {
public $auto_preferred = ",;\t.:|"; public $auto_preferred = ",;\t.:|";
/** /**
* Convert Encoding
* Should we convert the CSV character encoding? * Should we convert the CSV character encoding?
* Used for both parse and unparse operations.
* *
* @var bool * @var bool
*/ */
public $convert_encoding = false; public $convert_encoding = false;
/** /**
* Input Encoding
* Set the input encoding * Set the input encoding
* *
* @var string * @var string
@@ -184,7 +177,6 @@ class Csv {
public $input_encoding = 'ISO-8859-1'; public $input_encoding = 'ISO-8859-1';
/** /**
* Output Encoding
* Set the output encoding * Set the output encoding
* *
* @var string * @var string
@@ -202,15 +194,14 @@ class Csv {
public $use_mb_convert_encoding = false; public $use_mb_convert_encoding = false;
/** /**
* Linefeed
* Line feed characters used by unparse, save, and output methods * Line feed characters used by unparse, save, and output methods
* Popular choices are "\r\n" and "\n".
* *
* @var string * @var string
*/ */
public $linefeed = "\r"; public $linefeed = "\r";
/** /**
* Output Delimiter
* Sets the output delimiter used by the output method * Sets the output delimiter used by the output method
* *
* @var string * @var string
@@ -218,7 +209,6 @@ class Csv {
public $output_delimiter = ','; public $output_delimiter = ',';
/** /**
* Output filename
* Sets the output filename * Sets the output filename
* *
* @var string * @var string
@@ -226,7 +216,6 @@ class Csv {
public $output_filename = 'data.csv'; public $output_filename = 'data.csv';
/** /**
* Keep File Data
* keep raw file data in memory after successful parsing (useful for debugging) * keep raw file data in memory after successful parsing (useful for debugging)
* *
* @var bool * @var bool
@@ -270,7 +259,6 @@ class Csv {
public $error = 0; public $error = 0;
/** /**
* Error Information
* Detailed error information * Detailed error information
* *
* @var array * @var array
@@ -298,20 +286,23 @@ class Csv {
public $titles = array(); public $titles = array();
/** /**
* Data * Two-dimensional array of CSV data.
* Two-dimensional array of CSV data * The first dimension are the line numbers. Each line is represented as an array with field names as keys.
* *
* @var array * @var array<array>
*/ */
public $data = array(); public $data = array();
use DatatypeTrait; use DatatypeTrait;
/** /**
* Constructor
* Class constructor * Class constructor
* *
* @param string|null $input The CSV string or a direct file path * @param string|null $data The CSV string or a direct file path.
*
* WARNING: Supplying file paths here is
* deprecated. Use parseFile() instead.
*
* @param int|null $offset Number of rows to ignore from the * @param int|null $offset Number of rows to ignore from the
* beginning of the data * beginning of the data
* @param int|null $limit Limits the number of returned rows * @param int|null $limit Limits the number of returned rows
@@ -322,11 +313,11 @@ class Csv {
* successful parsing * successful parsing
* (useful for debugging) * (useful for debugging)
*/ */
public function __construct($input = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) { public function __construct($data = null, $offset = null, $limit = null, $conditions = null, $keep_file_data = null) {
$this->init($offset, $limit, $conditions, $keep_file_data); $this->init($offset, $limit, $conditions, $keep_file_data);
if (!empty($input)) { if (!empty($data)) {
$this->parse($input); $this->parse($data);
} }
} }
@@ -364,10 +355,12 @@ class Csv {
// ============================================== // ==============================================
/** /**
* Parse
* Parse a CSV file or string * Parse a CSV file or string
* *
* @param string|null $input The CSV string or a direct file path * @param string|null $dataString The CSV string or a direct file path
* WARNING: Supplying file paths here is
* deprecated and will trigger an
* E_USER_DEPRECATED error.
* @param int|null $offset Number of rows to ignore from the * @param int|null $offset Number of rows to ignore from the
* beginning of the data * beginning of the data
* @param int|null $limit Limits the number of returned rows to * @param int|null $limit Limits the number of returned rows to
@@ -377,23 +370,30 @@ class Csv {
* *
* @return bool True on success * @return bool True on success
*/ */
public function parse($input = null, $offset = null, $limit = null, $conditions = null) { public function parse($dataString = null, $offset = null, $limit = null, $conditions = null) {
if (is_null($input)) { if (is_null($dataString)) {
$input = $this->file; $this->data = $this->parseFile();
return $this->data !== false;
} }
if (empty($input)) { if (empty($dataString)) {
return false; return false;
} }
$this->init($offset, $limit, $conditions); $this->init($offset, $limit, $conditions);
if (strlen($input) <= PHP_MAXPATHLEN && is_readable($input)) { if (strlen($dataString) <= PHP_MAXPATHLEN && is_readable($dataString)) {
$this->file = $input; $this->file = $dataString;
$this->data = $this->_parse_file(); $this->data = $this->parseFile();
trigger_error(
'Supplying file paths to parse() will no longer ' .
'be supported in a future version of ParseCsv. ' .
'Use ->parseFile() instead.',
E_USER_DEPRECATED
);
} else { } else {
$this->file = null; $this->file = null;
$this->file_data = &$input; $this->file_data = &$dataString;
$this->data = $this->_parse_string(); $this->data = $this->_parse_string();
} }
@@ -401,8 +401,7 @@ class Csv {
} }
/** /**
* Save * Save changes, or write a new file and/or data.
* Save changes, or write a new file and/or data
* *
* @param string $file File location to save to * @param string $file File location to save to
* @param array $data 2D array of data * @param array $data 2D array of data
@@ -420,14 +419,15 @@ class Csv {
} }
$mode = FileProcessingModeEnum::getAppendMode($append); $mode = FileProcessingModeEnum::getAppendMode($append);
$is_php = preg_match('/\.php$/i', $file) ? true : false; $is_php = (bool) preg_match('/\.php$/i', $file);
return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode); return $this->_wfile($file, $this->unparse($data, $fields, $append, $is_php), $mode);
} }
/** /**
* Output * Generate a CSV-based string for output.
* Generate a CSV based string for output. *
* Useful for exports in web applications.
* *
* @param string|null $filename If a filename is specified here or in the * @param string|null $filename If a filename is specified here or in the
* object, headers and data will be output * object, headers and data will be output
@@ -471,11 +471,15 @@ class Csv {
} }
/** /**
* Encoding
* Convert character encoding * Convert character encoding
* *
* @param string|null $input Input character encoding, uses default if left blank * Specify the encoding to use for the next parsing or unparsing.
* Calling this function will not change the data held in the object immediately.
*
* @param string|null $input Input character encoding
* If the value null is passed, the existing input encoding remains set (default: ISO-8859-1).
* @param string|null $output Output character encoding, uses default if left blank * @param string|null $output Output character encoding, uses default if left blank
* If the value null is passed, the existing input encoding remains set (default: ISO-8859-1).
* *
* @return void * @return void
*/ */
@@ -491,15 +495,17 @@ class Csv {
} }
/** /**
* Auto * Auto-detect delimiter: Find delimiter by analyzing a specific number of
* Auto-Detect Delimiter: Find delimiter by analyzing a specific number of
* rows to determine most probable delimiter character * rows to determine most probable delimiter character
* *
* @param string|null $file Local CSV file * @param string|null $file Local CSV file
* @param bool $parse True/false parse file directly * Supplying CSV data (file content) here is deprecated.
* @param int|null $search_depth Number of rows to analyze * For CSV data, please use autoDetectionForDataString().
* @param string|null $preferred Preferred delimiter characters * Support for CSV data will be removed in v2.0.0.
* @param string|null $enclosure Enclosure character, default is double quote ("). * @param bool $parse True/false parse file directly
* @param int|null $search_depth Number of rows to analyze
* @param string|null $preferred Preferred delimiter characters
* @param string|null $enclosure Enclosure character, default is double quote (").
* *
* @return string|false The detected field delimiter * @return string|false The detected field delimiter
*/ */
@@ -532,6 +538,13 @@ class Csv {
$data = &$this->file_data; $data = &$this->file_data;
} }
$this->autoDetectionForDataString($data, $parse, $search_depth, $preferred, $enclosure);
return $this->delimiter;
}
public function autoDetectionForDataString($data, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) {
$this->file_data = &$data;
if (!$this->_detect_and_remove_sep_row_from_data($data)) { if (!$this->_detect_and_remove_sep_row_from_data($data)) {
$this->_guess_delimiter($search_depth, $preferred, $enclosure, $data); $this->_guess_delimiter($search_depth, $preferred, $enclosure, $data);
} }
@@ -585,23 +598,33 @@ class Csv {
// ============================================== // ==============================================
/** /**
* Parse File
* Read file to string and call _parse_string() * Read file to string and call _parse_string()
* *
* @param string|null $file Local CSV file * @param string|null $file Path to a CSV file.
* If configured in files such as php.ini,
* the path may also contain a protocol:
* https://example.org/some/file.csv
* *
* @return array|bool * @return array<array>|false
*/ */
protected function _parse_file($file = null) { public function parseFile($file = null) {
if (is_null($file)) { if (is_null($file)) {
$file = $this->file; $file = $this->file;
} }
if (empty($this->file_data)) { /**
$this->load_data($file); * @see self::keep_file_data
* Usually, _parse_string will clean this
* Instead of leaving stale data for the next parseFile call behind.
*/
if (empty($this->file_data) && !$this->loadFile($file)) {
return false;
} }
return !empty($this->file_data) ? $this->_parse_string() : false; if (empty($this->file_data)) {
return false;
}
return $this->data = $this->_parse_string();
} }
/** /**
@@ -615,7 +638,8 @@ class Csv {
* *
* @param string|null $data CSV data * @param string|null $data CSV data
* *
* @return array|false - 2D array with CSV data, or false on failure * @return array<array>|false
* 2D array with CSV data, or false on failure
*/ */
protected function _parse_string($data = null) { protected function _parse_string($data = null) {
if (empty($data)) { if (empty($data)) {
@@ -712,9 +736,8 @@ class Csv {
} else { } else {
$enclosed = false; $enclosed = false;
} }
// end of field/row/csv // end of field/row/csv
} elseif (($ch === $this->delimiter || $ch == "\n" || $ch == "\r" || $ch === false) && !$enclosed) { } elseif ((in_array($ch, [$this->delimiter, "\n", "\r", false], true)) && !$enclosed) {
$key = !empty($head[$col]) ? $head[$col] : $col; $key = !empty($head[$col]) ? $head[$col] : $col;
$row[$key] = $was_enclosed ? $current : trim($current); $row[$key] = $was_enclosed ? $current : trim($current);
$current = ''; $current = '';
@@ -832,9 +855,8 @@ class Csv {
$string .= implode($delimiter, $entry) . $this->linefeed; $string .= implode($delimiter, $entry) . $this->linefeed;
$entry = array(); $entry = array();
} }
// create data // create data
foreach ($data as $key => $row) { foreach ($data as $row) {
foreach (array_keys($fieldOrder) as $index) { foreach (array_keys($fieldOrder) as $index) {
$cell_value = $row[$index]; $cell_value = $row[$index];
$entry[] = $this->_enclose_value($cell_value, $delimiter); $entry[] = $this->_enclose_value($cell_value, $delimiter);
@@ -914,32 +936,71 @@ class Csv {
* This function load_data() is able to handle BOMs and encodings. The data * This function load_data() is able to handle BOMs and encodings. The data
* is stored within the $this->file_data class field. * is stored within the $this->file_data class field.
* *
* @param string|null $input local CSV file or CSV data as a string * @param string|null $input CSV file path or CSV data as a string
*
* Supplying CSV data (file content) here is deprecated.
* For CSV data, please use loadDataString().
* Support for CSV data will be removed in v2.0.0.
*
* @return bool True on success
* @deprecated Use loadDataString() or loadFile() instead.
*/
public function load_data($input = null) {
return $this->loadFile($input);
}
/**
* Load a file, but don't parse it.
*
* Only use this function if auto() and parseFile() don't handle your data well.
*
* This function is able to handle BOMs and encodings. The data
* is stored within the $this->file_data class field.
*
* @param string|null $file CSV file path
* *
* @return bool True on success * @return bool True on success
*/ */
public function load_data($input = null) { public function loadFile($file = null) {
$data = null; $data = null;
$file = null;
if (is_null($input)) { if (is_null($file)) {
$file = $this->file; $data = $this->_rfile($this->file);
} elseif (\strlen($input) <= PHP_MAXPATHLEN && file_exists($input)) { } elseif (\strlen($file) <= PHP_MAXPATHLEN && file_exists($file)) {
$file = $input; $data = $this->_rfile($file);
} else {
// It is CSV data as a string.
$data = $input;
}
if (!empty($data) || $data = $this->_rfile($file)) {
if ($this->file != $file) { if ($this->file != $file) {
$this->file = $file; $this->file = $file;
} }
} else {
// It is CSV data as a string.
if (preg_match('/\.php$/i', $file) && preg_match('/<\?.*?\?>(.*)/ms', $data, $strip)) { // WARNING:
$data = ltrim($strip[1]); // Supplying CSV data to load_data() will no longer
} // be supported in a future version of ParseCsv.
// This function will return false for invalid paths from v2.0.0 onwards.
// Use ->loadDataString() instead.
$data = $file;
}
return $this->loadDataString($data);
}
/**
* Load a data string, but don't parse it.
*
* Only use this function if autoDetectionForDataString() and parse() don't handle your data well.
*
* This function is able to handle BOMs and encodings. The data
* is stored within the $this->file_data class field.
*
* @param string|null $file_path CSV file path
*
* @return bool True on success
*/
public function loadDataString($data) {
if (!empty($data)) {
if (strpos($data, "\xef\xbb\xbf") === 0) { if (strpos($data, "\xef\xbb\xbf") === 0) {
// strip off BOM (UTF-8) // strip off BOM (UTF-8)
$data = substr($data, 3); $data = substr($data, 3);
@@ -989,7 +1050,7 @@ class Csv {
* @param array $row array with values from a row * @param array $row array with values from a row
* @param string|null $conditions specified conditions that the row must match * @param string|null $conditions specified conditions that the row must match
* *
* @return true of false * @return bool
*/ */
protected function _validate_row_conditions($row = array(), $conditions = null) { protected function _validate_row_conditions($row = array(), $conditions = null) {
if (!empty($row)) { if (!empty($row)) {
@@ -1047,6 +1108,8 @@ class Csv {
'is greater than or equals', 'is greater than or equals',
'contains', 'contains',
'does not contain', 'does not contain',
'is number',
'is not number',
); );
$operators_regex = array(); $operators_regex = array();
@@ -1088,6 +1151,10 @@ class Csv {
$op_equals = in_array($op, ['=', 'equals', 'is'], true); $op_equals = in_array($op, ['=', 'equals', 'is'], true);
if ($op_equals && $row[$field] == $value) { if ($op_equals && $row[$field] == $value) {
return '1'; return '1';
} elseif ($op_equals && $value == 'number' && is_numeric($row[$field])) {
return '1';
} elseif (($op == '!=' || $op == 'is not') && $value == 'number' && !is_numeric($row[$field])) {
return '1';
} elseif (($op == '!=' || $op == 'is not') && $row[$field] != $value) { } elseif (($op == '!=' || $op == 'is not') && $row[$field] != $value) {
return '1'; return '1';
} elseif (($op == '<' || $op == 'is less than') && $row[$field] < $value) { } elseif (($op == '<' || $op == 'is less than') && $row[$field] < $value) {
@@ -1116,7 +1183,7 @@ class Csv {
* *
* @param int $current_row the current row number being processed * @param int $current_row the current row number being processed
* *
* @return true of false * @return bool
*/ */
protected function _validate_offset($current_row) { protected function _validate_offset($current_row) {
return return
@@ -1164,14 +1231,14 @@ class Csv {
$file = $this->file; $file = $this->file;
} }
return $this->load_data($file); return $this->loadFile($file);
} }
return true; return true;
} }
/** /**
* Check if passed info might be delimiter * Check if passed info might be delimiter.
* Only used by find_delimiter * Only used by find_delimiter
* *
* @param string $char Potential field separating character * @param string $char Potential field separating character
@@ -1186,7 +1253,7 @@ class Csv {
$first = null; $first = null;
$equal = null; $equal = null;
$almost = false; $almost = false;
foreach ($array as $key => $value) { foreach ($array as $value) {
if ($first == null) { if ($first == null) {
$first = $value; $first = $value;
} elseif ($value == $first && $equal !== false) { } elseif ($value == $first && $equal !== false) {
@@ -1199,7 +1266,7 @@ class Csv {
} }
} }
if ($equal) { if ($equal || $depth === 1) {
$match = $almost ? 2 : 1; $match = $almost ? 2 : 1;
$pref = strpos($preferred, $char); $pref = strpos($preferred, $char);
$pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999'; $pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999';
@@ -1215,16 +1282,23 @@ class Csv {
/** /**
* Read local file. * Read local file.
* *
* @param string $file local filename * @param string $filePath local filename
* *
* @return string|false Data from file, or false on failure * @return string|false Data from file, or false on failure
*/ */
protected function _rfile($file) { protected function _rfile($filePath) {
if (is_readable($file)) { if (is_readable($filePath)) {
$data = file_get_contents($file); $data = file_get_contents($filePath);
if ($data === false) { if ($data === false) {
return false; return false;
} }
if (preg_match('/\.php$/i', $filePath) && preg_match('/<\?.*?\?>(.*)/ms', $data, $strip)) {
// Return section behind closing tags.
// This parsing is deprecated and will be removed in v2.0.0.
$data = ltrim($strip[1]);
}
return rtrim($data, "\r\n"); return rtrim($data, "\r\n");
} }
@@ -1336,7 +1410,7 @@ class Csv {
$is_newline = ($ch == "\n" && $pch != "\r") || $ch == "\r"; $is_newline = ($ch == "\n" && $pch != "\r") || $ch == "\r";
if ($ch == $enclosure) { if ($ch == $enclosure) {
if (!$enclosed || $nch != $enclosure) { if (!$enclosed || $nch != $enclosure) {
$enclosed = $enclosed ? false : true; $enclosed = !$enclosed;
} elseif ($enclosed) { } elseif ($enclosed) {
$i++; $i++;
} }

View File

@@ -19,7 +19,7 @@ trait DatatypeTrait {
* Check data type for one column. * Check data type for one column.
* Check for most commonly data type for one column. * Check for most commonly data type for one column.
* *
* @param array $datatypes * @param array $datatypes
* *
* @return string|false * @return string|false
*/ */
@@ -36,7 +36,6 @@ trait DatatypeTrait {
reset($typesFreq); reset($typesFreq);
return key($typesFreq); return key($typesFreq);
} }
/** /**

View File

@@ -1,4 +1,5 @@
<?php <?php
chdir(__DIR__ . '/..'); chdir(__DIR__ . '/..');
if (!file_exists('vendor/autoload.php')) { if (!file_exists('vendor/autoload.php')) {
`composer dump-autoload`; `composer dump-autoload`;

View File

@@ -0,0 +1 @@
C1,C2,C3
1 C1 C2 C3

View File

@@ -61,8 +61,13 @@ class ConstructTest extends TestCase {
$ob_get_clean = ob_get_clean(); $ob_get_clean = ob_get_clean();
$verb = strtok($script_file, '_.'); $verb = strtok($script_file, '_.');
if (!in_array($verb, ['download', 'save'], true)) { if (!in_array($verb, ['download', 'save'], TRUE)) {
$this->assertContains('<td>', $ob_get_clean); if (method_exists($this, 'assertStringContainsString')) {
$this->assertStringContainsString('<td>', $ob_get_clean);
}
else {
$this->assertContains('<td>', $ob_get_clean);
}
} }
} }
chdir('..'); chdir('..');

View File

@@ -17,17 +17,11 @@ class DataRowCountTest extends TestCase {
*/ */
protected $csv; protected $csv;
/** protected function setUp(): void {
* Setup
* Setup our test environment objects
*
* @access public
*/
public function setUp() {
$this->csv = new Csv(); $this->csv = new Csv();
} }
public function countRowsProvider() { public static function countRowsProvider() {
return [ return [
'auto-double-enclosure' => [ 'auto-double-enclosure' => [
'auto-double-enclosure.csv', 'auto-double-enclosure.csv',
@@ -53,22 +47,22 @@ class DataRowCountTest extends TestCase {
public function testGetTotalRowCountFromFile($file, $expectedRows) { public function testGetTotalRowCountFromFile($file, $expectedRows) {
$this->csv->heading = true; $this->csv->heading = true;
$this->csv->load_data(__DIR__ . '/fixtures/' . $file); $this->csv->load_data(__DIR__ . '/fixtures/' . $file);
$this->assertEquals($expectedRows, $this->csv->getTotalDataRowCount()); self::assertEquals($expectedRows, $this->csv->getTotalDataRowCount());
} }
public function testGetTotalRowCountMissingEndingLineBreak() { public function testGetTotalRowCountMissingEndingLineBreak() {
$this->csv->heading = false; $this->csv->heading = false;
$this->csv->enclosure = '"'; $this->csv->enclosure = '"';
$sInput = "86545235689,a\r\n34365587654,b\r\n13469874576,\"c\r\nd\""; $sInput = "86545235689,a\r\n34365587654,b\r\n13469874576,\"c\r\nd\"";
$this->csv->load_data($sInput); $this->csv->loadDataString($sInput);
$this->assertEquals(3, $this->csv->getTotalDataRowCount()); self::assertEquals(3, $this->csv->getTotalDataRowCount());
} }
public function testGetTotalRowCountSingleEnclosure() { public function testGetTotalRowCountSingleEnclosure() {
$this->csv->heading = false; $this->csv->heading = false;
$this->csv->enclosure = "'"; $this->csv->enclosure = "'";
$sInput = "86545235689,a\r\n34365587654,b\r\n13469874576,\'c\r\nd\'"; $sInput = "86545235689,a\r\n34365587654,b\r\n13469874576,\'c\r\nd\'";
$this->csv->load_data($sInput); $this->csv->loadDataString($sInput);
$this->assertEquals(3, $this->csv->getTotalDataRowCount()); $this->assertEquals(3, $this->csv->getTotalDataRowCount());
} }
@@ -76,7 +70,11 @@ class DataRowCountTest extends TestCase {
$this->csv->heading = false; $this->csv->heading = false;
$this->csv->enclosure = "'"; $this->csv->enclosure = "'";
$sInput = "86545235689"; $sInput = "86545235689";
$this->csv->load_data($sInput); $this->csv->loadDataString($sInput);
$this->assertEquals(1, $this->csv->getTotalDataRowCount()); $this->assertEquals(1, $this->csv->getTotalDataRowCount());
} }
public function testGetTotalRowCountNoData() {
self::assertFalse($this->csv->getTotalDataRowCount());
}
} }

View File

@@ -98,4 +98,3 @@ class ExampleStream {
return false; return false;
} }
} }

View File

@@ -11,11 +11,11 @@ use PHPUnit\Framework\TestCase;
*/ */
class OldRequireTest extends TestCase { class OldRequireTest extends TestCase {
protected function setUp() { protected function setUp(): void {
rename('vendor/autoload.php', '__autoload'); rename('vendor/autoload.php', '__autoload');
} }
protected function tearDown() { protected function tearDown(): void {
rename('__autoload', 'vendor/autoload.php'); rename('__autoload', 'vendor/autoload.php');
} }
@@ -23,7 +23,6 @@ class OldRequireTest extends TestCase {
* @runInSeparateProcess so that disabled autoloading has an effect * @runInSeparateProcess so that disabled autoloading has an effect
*/ */
public function testOldLibWithoutComposer() { public function testOldLibWithoutComposer() {
file_put_contents('__eval.php', '<?php require "parsecsv.lib.php"; new \ParseCsv\Csv;'); file_put_contents('__eval.php', '<?php require "parsecsv.lib.php"; new \ParseCsv\Csv;');
exec("php __eval.php", $output, $return_var); exec("php __eval.php", $output, $return_var);
unlink('__eval.php'); unlink('__eval.php');
@@ -35,7 +34,6 @@ class OldRequireTest extends TestCase {
* @runInSeparateProcess so that disabled autoloading has an effect * @runInSeparateProcess so that disabled autoloading has an effect
*/ */
public function testOldLibWithOldClassName() { public function testOldLibWithOldClassName() {
file_put_contents('__eval.php', '<?php require "parsecsv.lib.php"; new parseCSV;'); file_put_contents('__eval.php', '<?php require "parsecsv.lib.php"; new parseCSV;');
exec("php __eval.php", $output, $return_var); exec("php __eval.php", $output, $return_var);
unlink('__eval.php'); unlink('__eval.php');

View File

@@ -14,8 +14,10 @@ class OutputTest extends TestCase {
$csv = new Csv(); $csv = new Csv();
$data = [0 => ['a', 'b', 'c'], 1 => ['d', 'e', 'f']]; $data = [0 => ['a', 'b', 'c'], 1 => ['d', 'e', 'f']];
$fields = ['col1', 'col2', 'col3']; $fields = ['col1', 'col2', 'col3'];
ob_start();
$output = $csv->output('test.csv', $data, $fields, ','); $output = $csv->output('test.csv', $data, $fields, ',');
$expected = "col1,col2,col3\ra,b,c\rd,e,f\r"; $expected = "col1,col2,col3\ra,b,c\rd,e,f\r";
self::assertEquals($expected, ob_get_clean());
self::assertEquals($expected, $output); self::assertEquals($expected, $output);
} }
} }

View File

@@ -19,7 +19,7 @@ class ParseTest extends TestCase {
* *
* @access public * @access public
*/ */
public function setUp() { protected function setUp(): void {
$this->csv = new Csv(); $this->csv = new Csv();
} }
@@ -36,12 +36,12 @@ class ParseTest extends TestCase {
$this->csv->delimiter = ';'; $this->csv->delimiter = ';';
$this->csv->heading = false; $this->csv->heading = false;
$success = $this->csv->parse(str_repeat($content . ';', 500)); $success = $this->csv->parse(str_repeat($content . ';', 500));
$this->assertEquals(true, $success); self::assertEquals(true, $success);
$row = array_pop($this->csv->data); $row = array_pop($this->csv->data);
$expected_data = array_fill(0, 500, $content); $expected_data = array_fill(0, 500, $content);
$expected_data [] = ''; $expected_data[] = '';
$this->assertEquals($expected_data, $row); self::assertEquals($expected_data, $row);
} }
/** /**
@@ -57,13 +57,13 @@ class ParseTest extends TestCase {
$this->csv->output_encoding = 'UTF-8'; $this->csv->output_encoding = 'UTF-8';
$this->csv->auto($file); $this->csv->auto($file);
$this->assertEquals($this->_get_magazines_data(), $this->csv->data); self::assertEquals($this->_get_magazines_data(), $this->csv->data);
} }
/** /**
* @return array * @return array
*/ */
public function autoDetectionProvider() { public static function autoDetectionProvider() {
return [ return [
'UTF8_no_BOM' => [__DIR__ . '/../example_files/UTF-8_sep_row_but_no_BOM.csv'], 'UTF8_no_BOM' => [__DIR__ . '/../example_files/UTF-8_sep_row_but_no_BOM.csv'],
'UTF8' => [__DIR__ . '/../example_files/UTF-8_with_BOM_and_sep_row.csv'], 'UTF8' => [__DIR__ . '/../example_files/UTF-8_with_BOM_and_sep_row.csv'],
@@ -78,17 +78,19 @@ class ParseTest extends TestCase {
); );
$row = array_pop($this->csv->data); $row = array_pop($this->csv->data);
$expected_data = ['URL' => 'http://www.amazon.com/ROX-Ice-Ball-Maker-Original/dp/B00MX59NMQ/ref=sr_1_1?ie=UTF8&qid=1435604374&sr=8-1&keywords=rox,+ice+molds']; $expected_data = ['URL' => 'http://www.amazon.com/ROX-Ice-Ball-Maker-Original/dp/B00MX59NMQ/ref=sr_1_1?ie=UTF8&qid=1435604374&sr=8-1&keywords=rox,+ice+molds'];
$this->assertEquals($expected_data, $row); self::assertEquals($expected_data, $row);
} }
public function testAllNumericalCsv() { public function testAllNumericalCsv() {
$this->csv->heading = false; $this->csv->heading = false;
$sInput = "86545235689\r\n34365587654\r\n13469874576"; $sInput = "86545235689\r\n34365587654\r\n13469874576";
$this->assertEquals(false, $this->csv->auto($sInput)); self::assertEquals(false, $this->csv->autoDetectionForDataString($sInput));
$this->assertEquals(null, $this->csv->delimiter); self::assertEquals(null, $this->csv->delimiter);
$expected_data = explode("\r\n", $sInput); $expected_data = explode("\r\n", $sInput);
$actual_data = array_map('reset', $this->csv->data); $actual_data = array_map(function ($k) {
$this->assertEquals($expected_data, $actual_data); return reset($k);
}, $this->csv->data);
self::assertEquals($expected_data, $actual_data);
} }
public function testMissingEndingLineBreak() { public function testMissingEndingLineBreak() {
@@ -98,15 +100,19 @@ class ParseTest extends TestCase {
$expected_data = [86545235689, 34365587654, 13469874576]; $expected_data = [86545235689, 34365587654, 13469874576];
$actual_data = $this->invokeMethod($this->csv, '_parse_string', [$sInput]); $actual_data = $this->invokeMethod($this->csv, '_parse_string', [$sInput]);
$actual_column = array_map('reset', $actual_data); $actual_column = array_map(function ($k) {
$this->assertEquals($expected_data, $actual_column); return reset($k);
$this->assertEquals( }, $actual_data);
self::assertEquals($expected_data, $actual_column);
self::assertEquals(
[ [
'a', 'a',
'b', 'b',
"c\r\nd", "c\r\nd",
], ],
array_map('next', $actual_data) array_map(function ($el) {
return next($el);
}, $actual_data)
); );
} }
@@ -118,7 +124,16 @@ class ParseTest extends TestCase {
['SMS' => '6606'], ['SMS' => '6606'],
['SMS' => '7777'], ['SMS' => '7777'],
]; ];
$this->assertEquals($expected, $this->csv->data); self::assertEquals($expected, $this->csv->data);
}
public function testSingleRow() {
$this->csv->auto(__DIR__ . '/../example_files/single_row.csv');
self::assertEquals([], $this->csv->data, 'Single row is detected as header');
$this->csv->heading = false;
$this->csv->auto(__DIR__ . '/../example_files/single_row.csv');
$expected = [['C1', 'C2', 'C3']];
self::assertEquals($expected, $this->csv->data);
} }
public function testMatomoData() { public function testMatomoData() {
@@ -127,7 +142,7 @@ class ParseTest extends TestCase {
$this->csv->output_encoding = 'UTF-8'; $this->csv->output_encoding = 'UTF-8';
$this->csv->auto(__DIR__ . '/../example_files/Piwik_API_download.csv'); $this->csv->auto(__DIR__ . '/../example_files/Piwik_API_download.csv');
$aAction27 = array_column($this->csv->data, 'url (actionDetails 27)'); $aAction27 = array_column($this->csv->data, 'url (actionDetails 27)');
$this->assertEquals( self::assertEquals(
[ [
'http://application/_Main/_GraphicMeanSTD_MDI/btnConfBandOptions', 'http://application/_Main/_GraphicMeanSTD_MDI/btnConfBandOptions',
'', '',
@@ -137,7 +152,7 @@ class ParseTest extends TestCase {
); );
$aCity = array_column($this->csv->data, 'city'); $aCity = array_column($this->csv->data, 'city');
$this->assertEquals( self::assertEquals(
[ [
'São Paulo', 'São Paulo',
'Johannesburg', 'Johannesburg',
@@ -161,23 +176,27 @@ class ParseTest extends TestCase {
$this->csv->output_encoding = 'UTF-8'; $this->csv->output_encoding = 'UTF-8';
$this->csv->delimiter = ';'; $this->csv->delimiter = ';';
self::assertTrue($this->csv->load_data($string_with_bom)); self::assertTrue($this->csv->loadDataString($string_with_bom));
self::assertTrue($this->csv->parse($this->csv->file_data)); self::assertTrue($this->csv->parse($this->csv->file_data));
// This also tests if ::load_data removed the BOM from the data; // This also tests if ::load_data removed the BOM from the data;
// otherwise the 'title' column would have 3 extra bytes. // otherwise the 'title' column would have 3 extra bytes.
$this->assertEquals([ $this->assertEquals(
'title', [
'isbn', 'title',
'publishedAt', 'isbn',
], array_keys(reset($this->csv->data))); 'publishedAt',
],
array_keys(reset($this->csv->data)));
$titles = array_column($this->csv->data, 'title'); $titles = array_column($this->csv->data, 'title');
$this->assertEquals([ $this->assertEquals(
'Красивая кулинария', [
'The Wine Connoisseurs', 'Красивая кулинария',
'Weißwein', 'The Wine Connoisseurs',
], $titles); 'Weißwein',
],
$titles);
} }
public function testWithMultipleNewlines() { public function testWithMultipleNewlines() {
@@ -185,18 +204,20 @@ class ParseTest extends TestCase {
$aElse9 = array_column($this->csv->data, 'else9'); $aElse9 = array_column($this->csv->data, 'else9');
/** @noinspection SpellCheckingInspection */ /** @noinspection SpellCheckingInspection */
$this->assertEquals([ $this->assertEquals(
'Abweichung', [
'Abweichung', 'Abweichung',
'Abweichung', 'Abweichung',
'Alt', 'Abweichung',
'Fehlt', 'Alt',
'Neu', 'Fehlt',
'OK', 'Neu',
'Fehlt', 'OK',
'Fehlt', 'Fehlt',
'Fehlt', 'Fehlt',
], $aElse9); 'Fehlt',
],
$aElse9);
} }
/** /**
@@ -214,7 +235,7 @@ class ParseTest extends TestCase {
'price' => 'float', 'price' => 'float',
]; ];
$this->assertEquals($expected, $this->csv->data_types); self::assertEquals($expected, $this->csv->data_types);
} }
/** /**
@@ -222,21 +243,21 @@ class ParseTest extends TestCase {
*/ */
public function testAutoDetectFileHasHeading() { public function testAutoDetectFileHasHeading() {
$this->csv->auto(__DIR__ . '/fixtures/datatype.csv'); $this->csv->auto(__DIR__ . '/fixtures/datatype.csv');
$this->assertTrue($this->csv->autoDetectFileHasHeading()); self::assertTrue($this->csv->autoDetectFileHasHeading());
$this->csv->heading = false; $this->csv->heading = false;
$this->csv->auto(__DIR__ . '/fixtures/datatype.csv'); $this->csv->auto(__DIR__ . '/fixtures/datatype.csv');
$this->assertTrue($this->csv->autoDetectFileHasHeading()); self::assertTrue($this->csv->autoDetectFileHasHeading());
$this->csv->heading = false; $this->csv->heading = false;
$sInput = "86545235689\r\n34365587654\r\n13469874576"; $sInput = "86545235689\r\n34365587654\r\n13469874576";
$this->csv->auto($sInput); $this->csv->autoDetectionForDataString($sInput);
$this->assertFalse($this->csv->autoDetectFileHasHeading()); self::assertFalse($this->csv->autoDetectFileHasHeading());
$this->csv->heading = true; $this->csv->heading = true;
$sInput = "86545235689\r\n34365587654\r\n13469874576"; $sInput = "86545235689\r\n34365587654\r\n13469874576";
$this->csv->auto($sInput); $this->csv->autoDetectionForDataString($sInput);
$this->assertFalse($this->csv->autoDetectFileHasHeading()); self::assertFalse($this->csv->autoDetectFileHasHeading());
} }
/** /**
@@ -273,8 +294,7 @@ class ParseTest extends TestCase {
/** /**
* @return array * @return array
*/ */
public function autoQuotesDataProvider(): array public static function autoQuotesDataProvider(): array {
{
return array( return array(
array('auto-double-enclosure.csv', '"'), array('auto-double-enclosure.csv', '"'),
array('auto-single-enclosure.csv', "'"), array('auto-single-enclosure.csv', "'"),
@@ -292,16 +312,16 @@ class ParseTest extends TestCase {
public function testAutoQuotes($file, $enclosure) { public function testAutoQuotes($file, $enclosure) {
$csv = new Csv(); $csv = new Csv();
$csv->auto(__DIR__ . '/fixtures/' . $file, true, null, null, $enclosure); $csv->auto(__DIR__ . '/fixtures/' . $file, true, null, null, $enclosure);
$this->assertArrayHasKey('column1', $csv->data[0], 'Data parsed incorrectly with enclosure ' . $enclosure); self::assertArrayHasKey('column1', $csv->data[0], 'Data parsed incorrectly with enclosure ' . $enclosure);
$this->assertEquals('value1', $csv->data[0]['column1'], 'Data parsed incorrectly with enclosure ' . $enclosure); self::assertEquals('value1', $csv->data[0]['column1'], 'Data parsed incorrectly with enclosure ' . $enclosure);
} }
/** /**
* Call protected/private method of a class. * Call protected/private method of a class.
* *
* @param object $object Instantiated object that we will run method on. * @param object $object Instantiated object that we will run method on.
* @param string $methodName Method name to call * @param string $methodName Method name to call
* @param array $parameters Array of parameters to pass into method. * @param array $parameters Array of parameters to pass into method.
* *
* @return mixed Method return. * @return mixed Method return.
*/ */
@@ -314,7 +334,7 @@ class ParseTest extends TestCase {
} }
public function testWaiverFieldSeparator() { public function testWaiverFieldSeparator() {
$this->assertFalse($this->csv->auto(__DIR__ . '/../example_files/waiver_field_separator.csv')); self::assertFalse($this->csv->auto(__DIR__ . '/../example_files/waiver_field_separator.csv'));
$expected = [ $expected = [
'liability waiver', 'liability waiver',
'release of liability form', 'release of liability form',
@@ -323,6 +343,19 @@ class ParseTest extends TestCase {
'sample waiver form', 'sample waiver form',
]; ];
$actual = array_column($this->csv->data, 'keyword'); $actual = array_column($this->csv->data, 'keyword');
$this->assertSame($expected, $actual); self::assertSame($expected, $actual);
}
public function testEmptyInput() {
self::assertFalse($this->csv->parse(''));
self::assertFalse($this->csv->parse(null));
self::assertFalse($this->csv->parseFile(''));
self::assertFalse($this->csv->parseFile(null));
}
public function testParseFile() {
$data = $this->csv->parseFile(__DIR__ . '/fixtures/auto-double-enclosure.csv');
self::assertCount(2, $data);
self::assertEquals($data, $this->csv->data);
} }
} }

View File

@@ -13,9 +13,9 @@ class SaveTest extends TestCase {
private $temp_filename; private $temp_filename;
/** /**
* Setup our test environment objects; will be called before each test. * Set up our test environment objects; will be called before each test.
*/ */
public function setUp() { protected function setUp(): void {
$this->csv = new Csv(); $this->csv = new Csv();
$this->csv->auto(__DIR__ . '/../example_files/single_column.csv'); $this->csv->auto(__DIR__ . '/../example_files/single_column.csv');
@@ -51,11 +51,39 @@ class SaveTest extends TestCase {
public function testSaveWithNewHeader() { public function testSaveWithNewHeader() {
$this->csv->linefeed = "\n"; $this->csv->linefeed = "\n";
$this->csv->titles = array("NewTitle"); $this->csv->titles = ["NewTitle"];
$expected = "NewTitle\n0444\n5555\n"; $expected = "NewTitle\n0444\n5555\n";
$this->saveAndCompare($expected); $this->saveAndCompare($expected);
} }
public function testSaveWithDelimiterOfComma() {
$this->csv = new Csv();
$this->csv->heading = false;
$this->csv->delimiter = ",";
$this->csv->linefeed = "\n";
$this->csv->data = [
[
'3,21',
'Twitter',
'Monsieur',
'eat more vegan food',
],
[
'"9,72"',
'newsletter',
'Madame',
'"free travel"',
],
];
// Yep, these double quotes are what Excel and Open Office understand.
$expected =
'"3,21",Twitter,Monsieur,eat more vegan food' . "\n" .
'"""9,72""",newsletter,Madame,"""free travel"""' . "\n";
$actual = $this->csv->unparse();
self::assertSame($expected, $actual);
}
public function testSaveWithoutHeader() { public function testSaveWithoutHeader() {
$this->csv->linefeed = "\n"; $this->csv->linefeed = "\n";
$this->csv->heading = false; $this->csv->heading = false;
@@ -63,9 +91,9 @@ class SaveTest extends TestCase {
$this->saveAndCompare($expected); $this->saveAndCompare($expected);
} }
public function testAllQuotes() { public function testEncloseAllWithQuotes() {
$this->csv->enclose_all = true; $this->csv->enclose_all = true;
$expected = "\"SMS\"\r\"0444\"\r\"5555\"\r"; $expected = '"SMS"'."\r".'"0444"'."\r".'"5555"'."\r";
$this->saveAndCompare($expected); $this->saveAndCompare($expected);
} }

View File

@@ -11,7 +11,7 @@ use PHPUnit\Framework\TestCase;
*/ */
class StreamTest extends TestCase { class StreamTest extends TestCase {
public function setUp() { protected function setUp(): void {
static $done; static $done;
if ($done) { if ($done) {
// Make sure we register the stream just once - or PHP will scream. // Make sure we register the stream just once - or PHP will scream.

View File

@@ -14,13 +14,13 @@ class UnparseTest extends Testcase {
/** /**
* Setup our test environment objects; will be called before each test. * Setup our test environment objects; will be called before each test.
*/ */
public function setUp() { protected function setUp(): void {
$this->csv = new Csv(); $this->csv = new Csv();
$this->csv->auto(__DIR__ . '/fixtures/auto-double-enclosure.csv'); $this->csv->auto(__DIR__ . '/fixtures/auto-double-enclosure.csv');
} }
public function testUnparseWithParameters() { public function testUnparseWithParameters() {
$fields = array('a' => 'AA', 'b' => 'BB'); $fields = ['a' => 'AA', 'b' => 'BB'];
$data = [['a' => 'value1', 'b' => 'value2']]; $data = [['a' => 'value1', 'b' => 'value2']];
$csv_object = new Csv(); $csv_object = new Csv();
$csv_string = $csv_object->unparse($data, $fields); $csv_string = $csv_object->unparse($data, $fields);
@@ -43,34 +43,33 @@ class UnparseTest extends Testcase {
$this->unparseAndCompare($expected); $this->unparseAndCompare($expected);
} }
/**
* @doesNotPerformAssertions
*/
public function testUnparseDefaultWithoutHeading() { public function testUnparseDefaultWithoutHeading() {
$this->csv->heading = false; $this->csv->heading = false;
$this->csv->auto(__DIR__ . '/fixtures/auto-double-enclosure.csv'); $this->csv->auto(__DIR__ . '/fixtures/auto-double-enclosure.csv');
$expected = "column1,column2\rvalue1,value2\rvalue3,value4\r"; $expected = "column1,column2\rvalue1,value2\rvalue3,value4\r";
$this->unparseAndCompare($expected); $this->unparseAndCompare($expected);
} }
public function testUnparseRenameFields() { public function testUnparseRenameFields() {
$expected = "C1,C2\rvalue1,value2\rvalue3,value4\r"; $expected = "C1,C2\rvalue1,value2\rvalue3,value4\r";
$this->unparseAndCompare($expected, array("C1", "C2")); $this->unparseAndCompare($expected, ["C1", "C2"]);
} }
public function testReorderFields() { public function testReorderFields() {
$expected = "column2,column1\rvalue2,value1\rvalue4,value3\r"; $expected = "column2,column1\rvalue2,value1\rvalue4,value3\r";
$this->unparseAndCompare($expected, array("column2", "column1")); $this->unparseAndCompare($expected, ["column2", "column1"]);
} }
public function testSubsetFields() { public function testSubsetFields() {
$expected = "column1\rvalue1\rvalue3\r"; $expected = "column1\rvalue1\rvalue3\r";
$this->unparseAndCompare($expected, array("column1")); $this->unparseAndCompare($expected, ["column1"]);
} }
public function testReorderAndRenameFields() { public function testReorderAndRenameFields() {
$fields = array( $fields = ['column2' => 'C2', 'column1' => 'C1'];
'column2' => 'C2',
'column1' => 'C1',
);
$expected = "C2,C1\rvalue2,value1\rvalue4,value3\r"; $expected = "C2,C1\rvalue2,value1\rvalue4,value3\r";
$this->unparseAndCompare($expected, $fields); $this->unparseAndCompare($expected, $fields);
} }
@@ -100,7 +99,7 @@ class UnparseTest extends Testcase {
$this->unparseAndCompare($expected); $this->unparseAndCompare($expected);
} }
private function unparseAndCompare($expected, $fields = array()) { private function unparseAndCompare($expected, $fields = []) {
$str = $this->csv->unparse($this->csv->data, $fields); $str = $this->csv->unparse($this->csv->data, $fields);
$this->assertEquals($expected, $str); $this->assertEquals($expected, $str);
} }

View File

@@ -19,7 +19,7 @@ class BaseClass extends TestCase {
* Setup * Setup
* Setup our test environment objects * Setup our test environment objects
*/ */
protected function setUp() { protected function setUp(): void {
$this->csv = new Csv(); $this->csv = new Csv();
} }

View File

@@ -16,152 +16,135 @@ class DefaultValuesPropertiesTest extends TestCase {
*/ */
protected $csv = null; protected $csv = null;
/** protected function setUp(): void {
* Setup
* Setup our test environment objects
*
* @access public
*/
public function setUp() {
//setup parse CSV
$this->csv = new Csv(); $this->csv = new Csv();
} }
/**
* Tear down
* Tear down our test environment objects
*
* @access public
*/
public function tearDown() {
$this->csv = null;
}
public function test_heading_default() { public function test_heading_default() {
$this->assertTrue(is_bool($this->csv->heading)); self::assertTrue(is_bool($this->csv->heading));
$this->assertTrue($this->csv->heading); self::assertTrue($this->csv->heading);
} }
public function test_fields_default() { public function test_fields_default() {
$this->assertTrue(is_array($this->csv->fields)); self::assertTrue(is_array($this->csv->fields));
$this->assertCount(0, $this->csv->fields); self::assertCount(0, $this->csv->fields);
} }
public function test_sort_by_default() { public function test_sort_by_default() {
$this->assertNull($this->csv->sort_by); self::assertNull($this->csv->sort_by);
} }
public function test_sort_reverse_default() { public function test_sort_reverse_default() {
$this->assertTrue(is_bool($this->csv->sort_reverse)); self::assertTrue(is_bool($this->csv->sort_reverse));
$this->assertFalse($this->csv->sort_reverse); self::assertFalse($this->csv->sort_reverse);
} }
public function test_sort_type_default() { public function test_sort_type_default() {
$this->assertEquals('regular', $this->csv->sort_type); self::assertEquals('regular', $this->csv->sort_type);
} }
public function test_delimiter_default() { public function test_delimiter_default() {
$this->assertTrue(is_string($this->csv->delimiter)); self::assertTrue(is_string($this->csv->delimiter));
$this->assertEquals(',', $this->csv->delimiter); self::assertEquals(',', $this->csv->delimiter);
} }
public function test_enclosure_default() { public function test_enclosure_default() {
$this->assertTrue(is_string($this->csv->enclosure)); self::assertTrue(is_string($this->csv->enclosure));
$this->assertEquals('"', $this->csv->enclosure); self::assertEquals('"', $this->csv->enclosure);
} }
public function test_enclose_all_default() { public function test_enclose_all_default() {
$this->assertTrue(is_bool($this->csv->enclose_all)); self::assertTrue(is_bool($this->csv->enclose_all));
$this->assertFalse($this->csv->enclose_all); self::assertFalse($this->csv->enclose_all);
} }
public function test_conditions_default() { public function test_conditions_default() {
$this->assertNull($this->csv->conditions); self::assertNull($this->csv->conditions);
} }
public function test_offset_default() { public function test_offset_default() {
$this->assertNull($this->csv->offset); self::assertNull($this->csv->offset);
} }
public function test_limit_default() { public function test_limit_default() {
$this->assertNull($this->csv->limit); self::assertNull($this->csv->limit);
} }
public function test_auto_depth_default() { public function test_auto_depth_default() {
$this->assertTrue(is_numeric($this->csv->auto_depth)); self::assertTrue(is_numeric($this->csv->auto_depth));
$this->assertEquals(15, $this->csv->auto_depth); self::assertEquals(15, $this->csv->auto_depth);
} }
public function test_auto_non_chars_default() { public function test_auto_non_chars_default() {
$this->assertTrue(is_string($this->csv->auto_non_chars)); self::assertTrue(is_string($this->csv->auto_non_chars));
$this->assertEquals("a-zA-Z0-9\n\r", $this->csv->auto_non_chars); self::assertEquals("a-zA-Z0-9\n\r", $this->csv->auto_non_chars);
} }
public function test_auto_preferred_default() { public function test_auto_preferred_default() {
$this->assertTrue(is_string($this->csv->auto_preferred)); self::assertTrue(is_string($this->csv->auto_preferred));
$this->assertEquals(",;\t.:|", $this->csv->auto_preferred); self::assertEquals(",;\t.:|", $this->csv->auto_preferred);
} }
public function test_convert_encoding_default() { public function test_convert_encoding_default() {
$this->assertTrue(is_bool($this->csv->convert_encoding)); self::assertTrue(is_bool($this->csv->convert_encoding));
$this->assertFalse($this->csv->convert_encoding); self::assertFalse($this->csv->convert_encoding);
} }
public function test_input_encoding_default() { public function test_input_encoding_default() {
$this->assertTrue(is_string($this->csv->input_encoding)); self::assertTrue(is_string($this->csv->input_encoding));
$this->assertEquals('ISO-8859-1', $this->csv->input_encoding); self::assertEquals('ISO-8859-1', $this->csv->input_encoding);
} }
public function test_output_encoding_default() { public function test_output_encoding_default() {
$this->assertTrue(is_string($this->csv->output_encoding)); self::assertTrue(is_string($this->csv->output_encoding));
$this->assertEquals('ISO-8859-1', $this->csv->output_encoding); self::assertEquals('ISO-8859-1', $this->csv->output_encoding);
} }
public function test_linefeed_default() { public function test_linefeed_default() {
$this->assertTrue(is_string($this->csv->linefeed)); self::assertTrue(is_string($this->csv->linefeed));
$this->assertEquals("\r", $this->csv->linefeed); self::assertEquals("\r", $this->csv->linefeed);
} }
public function test_output_delimiter_default() { public function test_output_delimiter_default() {
$this->assertTrue(is_string($this->csv->output_delimiter)); self::assertTrue(is_string($this->csv->output_delimiter));
$this->assertEquals(',', $this->csv->output_delimiter); self::assertEquals(',', $this->csv->output_delimiter);
} }
public function test_output_filename_default() { public function test_output_filename_default() {
$this->assertTrue(is_string($this->csv->output_filename)); self::assertTrue(is_string($this->csv->output_filename));
$this->assertEquals('data.csv', $this->csv->output_filename); self::assertEquals('data.csv', $this->csv->output_filename);
} }
public function test_keep_file_data_default() { public function test_keep_file_data_default() {
$this->assertTrue(is_bool($this->csv->keep_file_data)); self::assertTrue(is_bool($this->csv->keep_file_data));
$this->assertFalse($this->csv->keep_file_data); self::assertFalse($this->csv->keep_file_data);
} }
public function test_file_default() { public function test_file_default() {
$this->assertNull($this->csv->file); self::assertNull($this->csv->file);
} }
public function test_file_data_default() { public function test_file_data_default() {
$this->assertNull($this->csv->file_data); self::assertNull($this->csv->file_data);
} }
public function test_error_default() { public function test_error_default() {
$this->assertTrue(is_numeric($this->csv->error)); self::assertTrue(is_numeric($this->csv->error));
$this->assertEquals(0, $this->csv->error); self::assertEquals(0, $this->csv->error);
} }
public function test_error_info_default() { public function test_error_info_default() {
$this->assertTrue(is_array($this->csv->error_info)); self::assertTrue(is_array($this->csv->error_info));
$this->assertCount(0, $this->csv->error_info); self::assertCount(0, $this->csv->error_info);
} }
public function test_titles_default() { public function test_titles_default() {
$this->assertTrue(is_array($this->csv->titles)); self::assertTrue(is_array($this->csv->titles));
$this->assertCount(0, $this->csv->titles); self::assertCount(0, $this->csv->titles);
} }
public function test_data_default() { public function test_data_default() {
$this->assertTrue(is_array($this->csv->data)); self::assertTrue(is_array($this->csv->data));
$this->assertCount(0, $this->csv->data); self::assertCount(0, $this->csv->data);
} }
} }

View File

@@ -25,7 +25,7 @@ class OffsetTest extends BaseClass {
$this->assertEquals($expected, $actual); $this->assertEquals($expected, $actual);
} }
public function numberRangeZeroToFourProvider() { public static function numberRangeZeroToFourProvider() {
return array_map(function ($number) { return array_map(function ($number) {
return [$number]; return [$number];
}, range(0, 4)); }, range(0, 4));
@@ -34,7 +34,7 @@ class OffsetTest extends BaseClass {
/** /**
* @dataProvider numberRangeZeroToFourProvider * @dataProvider numberRangeZeroToFourProvider
* *
* @param integer $offset * @param int $offset
*/ */
public function testOffsetOfOneNoHeader($offset) { public function testOffsetOfOneNoHeader($offset) {
$this->csv->offset = $offset; $this->csv->offset = $offset;

View File

@@ -41,7 +41,7 @@ class PublicPropertiesTest extends TestCase {
* *
* @access public * @access public
*/ */
public function setUp() { protected function setUp(): void {
//setup parse CSV //setup parse CSV
$this->csv = new Csv(); $this->csv = new Csv();
@@ -58,7 +58,7 @@ class PublicPropertiesTest extends TestCase {
* *
* @access public * @access public
*/ */
public function tearDown() { protected function tearDown(): void {
$this->csv = null; $this->csv = null;
$this->reflection = null; $this->reflection = null;
$this->properties = null; $this->properties = null;

View File

@@ -7,26 +7,48 @@ class SortByTest extends BaseClass {
public function testSortByRating() { public function testSortByRating() {
$this->csv->sort_by = 'rating'; $this->csv->sort_by = 'rating';
$this->csv->conditions = 'title does not contain Blood'; $this->csv->conditions = 'title does not contain Blood';
$this->_compareWithExpected([ if (!preg_match('/^8\.2\./', phpversion()) && !preg_match('/^8\.3\./', phpversion()))
// Rating 0 $this->_compareWithExpected([
'The Killing Kind', // Rating 0
'The Third Secret', 'The Killing Kind',
'The Third Secret',
// Rating 3 // Rating 3
'The Last Templar', 'The Last Templar',
'The Broker (Paperback)', 'The Broker (Paperback)',
// Rating 4 // Rating 4
'Deception Point (Paperback)', 'Deception Point (Paperback)',
'The Rule of Four (Paperback)', 'The Rule of Four (Paperback)',
'The Da Vinci Code (Hardcover)', 'The Da Vinci Code (Hardcover)',
// Rating 5 // Rating 5
'State of Fear (Paperback)', 'State of Fear (Paperback)',
'Prey', 'Prey',
'Digital Fortress : A Thriller (Mass Market Paperback)', 'Digital Fortress : A Thriller (Mass Market Paperback)',
'Angels & Demons (Mass Market Paperback)', 'Angels & Demons (Mass Market Paperback)',
]); ]);
else
$this->_compareWithExpected([
// Rating 0
'The Killing Kind',
'The Third Secret',
// Rating 3
'The Last Templar',
'The Broker (Paperback)',
// Rating 4
'Deception Point (Paperback)',
'The Rule of Four (Paperback)',
'The Da Vinci Code (Hardcover)',
// Rating 5
'Angels & Demons (Mass Market Paperback)',
'State of Fear (Paperback)',
'Prey',
'Digital Fortress : A Thriller (Mass Market Paperback)',
]);
} }
public function testReverseSortByRating() { public function testReverseSortByRating() {

18
tests/rector.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DataProviderAnnotationToAttributeRector;
use Rector\PHPUnit\AnnotationsToAttributes\Rector\ClassMethod\DependsAnnotationWithValueToAttributeRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\AddCoversClassAttributeRector;
use Rector\PHPUnit\PHPUnit60\Rector\ClassMethod\AddDoesNotPerformAssertionToNonAssertingTestRector;
return static function(RectorConfig $rectorConfig): void {
if (class_exists(AddCoversClassAttributeRector::class)) {
$rectorConfig->rule(AddCoversClassAttributeRector::class);
}
$rectorConfig->rule(DataProviderAnnotationToAttributeRector::class);
$rectorConfig->rule(AddDoesNotPerformAssertionToNonAssertingTestRector::class);
$rectorConfig->rule(DependsAnnotationWithValueToAttributeRector::class);
};