diff --git a/changelog.md b/changelog.md index 2b82dc4..2db274f 100644 --- a/changelog.md +++ b/changelog.md @@ -2,9 +2,12 @@ ## Changelog -See GitHub for [the complete changelog](https://github.com/webaware/events-manager-import-export/blob/master/changelog.md). +## Changelog 0.0.20, 2023-12-03 +* Fix export to export all events (not just future events) +* changed: parsecsv upgraded to v1.3.2 +* added export CSV option -### 0.0.12, soon... +### 0.0.12 * changed: requires PHP 5.6+ (recommend PHP 7.2+) * changed: Google Maps lookup is removed for now, will return soon as an Ajax-driven process post-import diff --git a/events-manager-import-export.php b/events-manager-import-export.php index 3b0730f..8f85092 100644 --- a/events-manager-import-export.php +++ b/events-manager-import-export.php @@ -3,7 +3,7 @@ Plugin Name: Events Manager Import Export Plugin URI: https://github.com/webaware/events-manager-import-export Description: import and export function for Events Manager -Version: 0.0.12 +Version: 0.0.13 Author: WebAware Author URI: https://shop.webaware.com.au/ Text Domain: events-manager-import-export diff --git a/includes/class.Exporter.php b/includes/class.Exporter.php index 6f2e352..d0c1391 100644 --- a/includes/class.Exporter.php +++ b/includes/class.Exporter.php @@ -22,7 +22,7 @@ public function render() { * export the data in selected format */ public function export() { - $EM_Events = EM_Events::get(); + $EM_Events = EM_Events::get(array('scope' => 'all')); $format = isset($_POST['exp_format']) ? wp_unslash($_POST['exp_format']) : ''; @@ -424,5 +424,70 @@ public function exportEventEspresso($EM_Events) { exit; } - + + /** + * export data in Event CSV format + * @param EM_Events $EM_Events + */ + public function exportCSV($EM_Events) { + if (isset($_REQUEST['plaintext']) && $_REQUEST['plaintext'] === '1') { + header('Content-Type: text/plain; charset=utf-8'); + } + else { + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="events.csv"'); + } + + nocache_headers(); + + // character conversion arrays + $charFrom = ['\\', ';', ',', "\n", "\t"]; + $charTo = ['\\\\', '\;', '\,', '\\n', '\\t']; + // send header row + + echo "uid,summary,dtstart,dtend,dtformat,categories,post_content,location_name,location_address,location_town,location_state,location_postcode,location_country,location_latitude,location_longitude\r\n"; + + foreach ($EM_Events as $EM_Event) { + echo $EM_Event->event_id, ','; // event_identifier + + echo text_to_csv($EM_Event->event_name), ','; // event_summary + $gmtOffset = HOUR_IN_SECONDS * get_option('gmt_offset'); + echo text_to_csv(date('Y-m-d', $EM_Event->start - $gmtOffset)), ','; // start_date + echo text_to_csv(date('Y-m-d', $EM_Event->end - $gmtOffset)), ','; // end_date + echo text_to_csv("Y-m-d"), ','; // dtformat + // get categories as comma-separated list + $cats = $EM_Event->get_categories()->categories; + if (count($cats) > 0) { + $categories = []; + foreach ($cats as $cat) { + $categories[] = str_replace($charFrom, $charTo, $cat->output('#_CATEGORYNAME')); + } + echo text_to_csv(implode(',', $categories)), ','; + } + else { + echo ','; + } + echo text_to_csv(preg_replace('/\s+/', ' ', strip_tags($EM_Event->post_content))), ','; // event_desc + + $location = $EM_Event->get_location(); + if (is_object($location)) { + echo text_to_csv($location->location_name), ',';// address + echo text_to_csv($location->location_address), ','; // address + echo text_to_csv($location->location_town), ',';// city + echo text_to_csv($location->location_state), ','; // state + echo text_to_csv($location->location_postcode), ','; // zip + echo text_to_csv($location->get_country()), ',';// country + echo text_to_csv($location->latitude), ','; // country + echo text_to_csv($location->longitude), ','; // country + } + else { + echo ',,,,,,,'; + } + + echo "\r\n"; + + } + + exit; + } } diff --git a/includes/class.Importer.php b/includes/class.Importer.php index ccce16b..befd3c8 100644 --- a/includes/class.Importer.php +++ b/includes/class.Importer.php @@ -368,7 +368,7 @@ protected function importEventsCSV($filepath) { while ($line = fgets($fp)) { $line = "\n$line\n"; // fix up line so that it can be parsed correctly - $cols = $csv->parse_string($line); + $cols = $csv->parseFile($line); if ($cols) { $rows++; diff --git a/lib/parsecsv/.githup/workflows/phpunit.yml b/lib/parsecsv/.githup/workflows/phpunit.yml new file mode 100644 index 0000000..06c9759 --- /dev/null +++ b/lib/parsecsv/.githup/workflows/phpunit.yml @@ -0,0 +1,43 @@ +--- +name: PHPUnit +on: + push: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php_version: + - "8.2" + - "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@v2 + 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 diff --git a/lib/parsecsv/ChangeLog.txt b/lib/parsecsv/ChangeLog.txt deleted file mode 100644 index 76ae0ee..0000000 --- a/lib/parsecsv/ChangeLog.txt +++ /dev/null @@ -1,288 +0,0 @@ -ParseCSV dev-master ------------------------------------ -Date: unreleased - -- New function getTotalDataRowCount() - useful if - $limit is set - see pull request #122. - -- Dropped support for PHP 5.4 - -- Added support for Laravel-style collections via the - new getCollection() function - see - https://github.com/parsecsv/parsecsv-for-php/pull/134 - ------------------------------------ - - -ParseCSV 1.0.0 ------------------------------------ -Date: 3-March-2018 - -- Renamed class from parseCSV to Csv and added name- - space "ParseCsv" for PSR compliance. - -- Added support for MS Excel's "sep=" to detect the - delimiter (Issue #60). - -- Added data type detection - function getDatatypes() - guesses the type of each column. - -- MIME: output() sends correct MIME type to browser - if the separator is a tab char (Issue #79). - -- Added support for mb_convert_encoding() instead of - iconv() - see issue #109. - -- A number of minor bug fixes - see GitHub issues. - -- Added many more unit tests. - ------------------------------------ - - -parseCSV 0.4.3 beta ------------------------------------ -Date: 1-July-2008 - -- Issue #4. Added an option for setting sorting - type behavior when sorting data. - Simply set $csv->sort_type to "regular", "numeric", - or "string". - -- Issue #6. Raw loaded file data is now cleared from - file_data property when it has been successfully - parsed to keep parseCSV's memory footprint to a - minimum. Specifically handy when using multiple - instances of parseCSV to process large files. - ------------------------------------ - - -parseCSV 0.4.2 beta ------------------------------------ -Date: 31-May-2008 - -- IMPORTANT! If you're using the output(), - method please note that the first parameter - has been completely removed as it was - technically just useless. Instead, the second - parameter (filename) doubles as its replacement. - Simply put, if filename is not set or null, the - output() method will not output a downloadable - file. Please update your existing code - when using 0.4.2 and later :) - -- Small fix to the headers sent by the output() - method. - -- Added a download example using the output() - method to the examples folder. - ------------------------------------ - - -parseCSV 0.4.1 beta ------------------------------------ -Date: 29-May-2008 - -- Fixed a small bug in how the output() method - handles input data. - ------------------------------------ - - -parseCSV 0.4 beta ------------------------------------ -Date: 11-Apr-2008 - -- Error reporting for files/data which is corrupt - or has formatting errors like using double - quotes in a field without enclosing quotes. Or - not escaping double quotes with a second one. - -- parse() method does not require input anymore - if the "$object->file" property has been set. - -I'm calling this a beta release due to the heavy -modifications to the core parsing logic required -for error reporting to work. I have tested the -new code quite extensively, I'm fairly confident -that it still parses exactly as it always has. - -The second reason I'm calling it a beta release -is cause I'm sure the error reporting code will -need more refinements and tweaks to detect more -types of errors, as it's only picking two types -or syntax errors right now. However, it seems -these two are the most common errors that you -would be likely to come across. - ------------------------------------ - - -parseCSV 0.3.2 ------------------------------------ -Date: 1-Apr-2008 - -This is primarily a bug-fix release for a critical -bug which was brought to my attention. - -- Fixed a critical bug in conditions parsing which - would generate corrupt matching patterns causing - the condition(s) to not work at all in some - situations. - -- Fixed a small code error which would cause PHP to - generate a invalid offset notice when zero length - values were fed into the unparse() method to - generate CSV data from an array. - -Notice: If you have been using the "parsecsv-stable" -branch as an external in any of your projects, -please use the "stable/parsecsv" branch from this -point on as I will eventually remove the former due -to it's stupid naming. - ------------------------------------ - - -parseCSV 0.3.1 ------------------------------------ -Date: 1-Sep-2007 - -- Small change to default output settings to - conform with RFC 4180 (http://rfc.net/rfc4180.html). - Only the LF (line feed) character was used - by default to separate rows, rather than - CRLF (carriage return & line feed). - ------------------------------------ - - -parseCSV 0.3.0 ------------------------------------ -Date: 9-Aug-2007 - -- Changed to the MIT license. - -- Added offset and limit options. - -- Added SQL-like conditions for quickly - filtering out entries. Documentation on the - condition syntax is forthcoming. - -- Small parsing modification to comply - with some recent changes to the specifications - outlined on Wikipedia's Comma-separated values - article. - -- Minor changes and optimizations, and a few - spelling corrections. Oops :) - -- Included more complex code examples in the - parseCSV download. - ------------------------------------ - - -parseCSV 0.2.1 ------------------------------------ -Date: 8-Aug-2007 - -- Fixed stupid code which caused auto function - to not work in some situations. - ------------------------------------ - - -parseCSV 0.2.0 beta ------------------------------------ -Date: 2-Jan-2007 - -- Added auto() function to automatically detect - delimiter character. - Useful for user upload in case delimiter is - comma (,), tab, or semi-colon (;). Some - versions of MS Excel for Windows use - semi-colons instead of commas when saving to - CSV files. - It uses a process of elimination to eliminate - characters that can not be the delimiter, - so it should work on all CSV-structured files - almost no matter what the delimiter is. - -- Generally updated some of the core workings - to increase performance, and offer better - support for large (1MB and up) files. - -- Added code examples to header comment. - ------------------------------------ - - -parseCSV 0.1.6 beta ------------------------------------ -Date: 22-Dec-2006 - -- Updated output() function. - ------------------------------------ - - -parseCSV 0.1.5 beta ------------------------------------ -Date: 22-Dec-2006 - -- Added output() function for easy output to - browser, for downloading features for example. - ------------------------------------ - - -parseCSV 0.1.4 beta ------------------------------------ -Date: 17-Dec-2006 - -- Minor changes and fixes - ------------------------------------ - - -parseCSV 0.1.3 beta ------------------------------------ -Date: 17-Dec-2006 - -- Added GPL v2.0 license. - ------------------------------------ - - -parseCSV 0.1.2 beta ------------------------------------ -Date: 17-Dec-2006 - -- Added encoding() function for easier character - encoding configuration. - ------------------------------------ - - -parseCSV 0.1.1 beta ------------------------------------ -Date: 24-Nov-2006 - -- Added support for a PHP die command on first - line of csv files if they have a .php extension - to protect secure data from being displayed - directly to the browser. - ------------------------------------ - - -parseCSV 0.1 beta ------------------------------------ -Date: 23-Nov-2006 - -- Initial release - ------------------------------------ diff --git a/lib/parsecsv/README.md b/lib/parsecsv/README.md new file mode 100644 index 0000000..b2ebfb2 --- /dev/null +++ b/lib/parsecsv/README.md @@ -0,0 +1,247 @@ +# 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 +fully conforms to the specifications outlined on the on the +[Wikipedia article][CSV] (and thus RFC 4180). It has many advanced features which help make your +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()`, ``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 +and third-party support for handling CSV data in PHP. + +[csv]: http://en.wikipedia.org/wiki/Comma-separated_values + +## Features + +* ParseCsv is a complete and fully featured CSV solution for PHP +* Supports enclosed values, enclosed commas, double quotes and new lines. +* Automatic delimiter character detection. +* Sort data by specific fields/columns. +* Easy data manipulation. +* Basic SQL-like _conditions_, _offset_ and _limit_ options for filtering + data. +* Error detection for incorrectly formatted input. It attempts to be + intelligent, but can not be trusted 100% due to the structure of CSV, and + how different programs like Excel for example outputs CSV data. +* Support for character encoding conversion using PHP's + `iconv()` and `mb_convert_encoding()` functions. +* Supports PHP 5.5 and higher. + It certainly works with PHP 7.2 and all versions in between. + +## Installation + +Installation is easy using Composer. Just run the following on the +command line: +``` +composer require parsecsv/php-parsecsv +``` + +If you don't use a framework such as Drupal, Laravel, Symfony, Yii etc., +you may have to manually include Composer's autoloader file in your PHP +script: +```php +require_once __DIR__ . '/vendor/autoload.php'; +``` + +#### Without composer +Not recommended, but technically possible: you can also clone the +repository or extract the +[ZIP](https://github.com/parsecsv/parsecsv-for-php/archive/master.zip). +To use ParseCSV, you then have to add a `require 'parsecsv.lib.php';` line. + +## Example Usage + +**Parse a tab-delimited CSV file with encoding conversion** + +```php +$csv = new \ParseCsv\Csv(); +$csv->encoding('UTF-16', 'UTF-8'); +$csv->delimiter = "\t"; +$csv->parseFile('data.tsv'); +print_r($csv->data); +``` + +**Auto-detect field delimiter character** + +```php +$csv = new \ParseCsv\Csv(); +$csv->auto('data.csv'); +print_r($csv->data); +``` + +**Parse data with offset** +* ignoring the first X (e.g. two) rows +```php +$csv = new \ParseCsv\Csv(); +$csv->offset = 2; +$csv->parseFile('data.csv'); +print_r($csv->data); +``` + +**Limit the number of returned data rows** +```php +$csv = new \ParseCsv\Csv(); +$csv->limit = 5; +$csv->parseFile('data.csv'); +print_r($csv->data); +``` + +**Get total number of data rows without parsing whole data** +* Excluding heading line if present (see $csv->header property) +```php +$csv = new \ParseCsv\Csv(); +$csv->loadFile('data.csv'); +$count = $csv->getTotalDataRowCount(); +print_r($count); +``` + +**Get most common data type for each column** + +```php +$csv = new \ParseCsv\Csv('data.csv'); +$csv->getDatatypes(); +print_r($csv->data_types); +``` + +**Modify data in a CSV file** + +Change data values: +```php +$csv = new \ParseCsv\Csv(); +$csv->sort_by = 'id'; +$csv->parseFile('data.csv'); +# "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->save(); +``` + +Enclose each data value by quotes: +```php +$csv = new \ParseCsv\Csv(); +$csv->parseFile('data.csv'); +$csv->enclose_all = true; +$csv->save(); +``` + +**Replace field names or set ones if missing** + +```php +$csv = new \ParseCsv\Csv(); +$csv->fields = ['id', 'name', 'category']; +$csv->parseFile('data.csv'); +``` + +**Add row/entry to end of CSV file** + +_Only recommended when you know the exact structure of the file._ + +```php +$csv = new \ParseCsv\Csv(); +$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 +a file and download it** + +Your web app users would call this an export. + +```php +$csv = new \ParseCsv\Csv(); +$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. + +## Test coverage + +All tests are located in the `tests` directory. To execute tests, run the following commands: + +````bash +composer install +composer run test +```` + +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 + +* ParseCsv is based on the concept of [Ming Hong Ng][ming]'s [CsvFileParser][] + class. + +[ming]: http://minghong.blogspot.com/ +[CsvFileParser]: http://minghong.blogspot.com/2006/07/csv-parser-for-php.html + + +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. + +Please find a complete list on the project's [contributors][] page. + +[contributors]: https://github.com/parsecsv/parsecsv-for-php/graphs/contributors + + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/parsecsv/contribute)] + +#### Individuals + + + +#### 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)] + + + + + + + + + + + + +## License + +(The MIT license) + +Copyright (c) 2014 Jim Myhrberg. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +[![Build Status](https://travis-ci.org/parsecsv/parsecsv-for-php.svg?branch=master)](https://travis-ci.org/parsecsv/parsecsv-for-php) diff --git a/lib/parsecsv/composer.json b/lib/parsecsv/composer.json new file mode 100644 index 0000000..cbbaca0 --- /dev/null +++ b/lib/parsecsv/composer.json @@ -0,0 +1,57 @@ +{ + "name": "parsecsv/php-parsecsv", + "description": "CSV data parser for PHP", + "license": "MIT", + "authors": [ + { + "name": "Jim Myhrberg", + "email": "contact@jimeh.me" + }, + { + "name": "William Knauss", + "email": "will.knauss@gmail.com" + }, + { + "name": "Susann Sgorzaly", + "homepage": "https://github.com/susgo" + }, + { + "name": "Christian Bläul", + "homepage": "https://github.com/Fonata" + } + ], + "autoload": { + "psr-4": { + "ParseCsv\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "ParseCsv\\tests\\": "tests" + } + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^6", + "squizlabs/php_codesniffer": "^3.5" + }, + "suggest": { + "illuminate/support": "Fluent array interface for map functions" + }, + "extra": { + "branch-alias": { + "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": { + "issues": "https://github.com/parsecsv/parsecsv-for-php/issues", + "source": "https://github.com/parsecsv/parsecsv-for-php" + } +} diff --git a/lib/parsecsv/src/Csv.php b/lib/parsecsv/src/Csv.php index 0980a93..c7fa5be 100644 --- a/lib/parsecsv/src/Csv.php +++ b/lib/parsecsv/src/Csv.php @@ -49,7 +49,7 @@ class Csv { */ /** - * Heading + * Header row: * Use first line/entry as field names * * @var bool @@ -57,7 +57,6 @@ class Csv { public $heading = true; /** - * Fields * Override field names * * @var array @@ -65,7 +64,6 @@ class Csv { public $fields = array(); /** - * Sort By * Sort CSV by this field * * @var string|null @@ -73,15 +71,13 @@ class Csv { public $sort_by = null; /** - * Sort Reverse - * Reverse the sort function + * Reverse the sort direction * * @var bool */ public $sort_reverse = false; /** - * Sort Type * Sort behavior passed to sort methods * * regular = SORT_REGULAR @@ -93,31 +89,34 @@ class Csv { public $sort_type = SortEnum::SORT_TYPE_REGULAR; /** - * Delimiter - * Delimiter character + * Field delimiter character * * @var string */ public $delimiter = ','; /** - * Enclosure * Enclosure character * + * This is useful for cell values that are either multi-line + * or contain the field delimiter character. + * * @var string */ 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 */ public $enclose_all = false; /** - * Conditions * Basic SQL-Like conditions for row matching * * @var string|null @@ -125,7 +124,6 @@ class Csv { public $conditions = null; /** - * Offset * Number of rows to ignore from beginning of data. If present, the heading * row is also counted (if $this->heading == true). In other words, * $offset == 1 and $offset == 0 have the same meaning in that situation. @@ -135,7 +133,6 @@ class Csv { public $offset = null; /** - * Limit * Limits the number of returned rows to the specified amount * * @var int|null @@ -143,7 +140,6 @@ class Csv { public $limit = null; /** - * Auto Depth * Number of rows to analyze when attempting to auto-detect delimiter * * @var int @@ -151,7 +147,6 @@ class Csv { public $auto_depth = 15; /** - * Auto Non Chars * Characters that should be ignored when attempting to auto-detect delimiter * * @var string @@ -159,7 +154,6 @@ class Csv { public $auto_non_chars = "a-zA-Z0-9\n\r"; /** - * Auto Preferred * preferred delimiter characters, only used when all filtering method * returns multiple possible delimiters (happens very rarely) * @@ -168,15 +162,14 @@ class Csv { public $auto_preferred = ",;\t.:|"; /** - * Convert Encoding - * Should we convert the csv encoding? + * Should we convert the CSV character encoding? + * Used for both parse and unparse operations. * * @var bool */ public $convert_encoding = false; /** - * Input Encoding * Set the input encoding * * @var string @@ -184,7 +177,6 @@ class Csv { public $input_encoding = 'ISO-8859-1'; /** - * Output Encoding * Set the output encoding * * @var string @@ -202,15 +194,14 @@ class Csv { public $use_mb_convert_encoding = false; /** - * Linefeed * Line feed characters used by unparse, save, and output methods + * Popular choices are "\r\n" and "\n". * * @var string */ public $linefeed = "\r"; /** - * Output Delimiter * Sets the output delimiter used by the output method * * @var string @@ -218,7 +209,6 @@ class Csv { public $output_delimiter = ','; /** - * Output filename * Sets the output filename * * @var string @@ -226,7 +216,6 @@ class Csv { public $output_filename = 'data.csv'; /** - * Keep File Data * keep raw file data in memory after successful parsing (useful for debugging) * * @var bool @@ -270,7 +259,6 @@ class Csv { public $error = 0; /** - * Error Information * Detailed error information * * @var array @@ -278,54 +266,69 @@ class Csv { public $error_info = array(); /** - * Titles - * CSV titles if they exists + * $titles has 4 distinct tasks: + * 1. After reading in CSV data, $titles will contain the column headers + * present in the data. + * + * 2. It defines which fields from the $data array to write e.g. when + * calling unparse(), and in which order. This lets you skip columns you + * don't want in your output, but are present in $data. + * See examples/save_to_file_without_header_row.php. + * + * 3. It lets you rename columns. See StreamTest::testWriteStream for an + * example. + * + * 4. When writing data and $header is true, then $titles is also used for + * the first row. * * @var 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 */ public $data = array(); use DatatypeTrait; /** - * Constructor * Class constructor * - * @param string|null $input The CSV string or a direct file path - * @param integer|null $offset Number of rows to ignore from the - * beginning of the data - * @param integer|null $limit Limits the number of returned rows - * to specified amount - * @param string|null $conditions Basic SQL-like conditions for row - * matching - * @param null|true $keep_file_data Keep raw file data in memory after - * successful parsing - * (useful for debugging) + * @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 + * beginning of the data + * @param int|null $limit Limits the number of returned rows + * to specified amount + * @param string|null $conditions Basic SQL-like conditions for row + * matching + * @param null|true $keep_file_data Keep raw file data in memory after + * successful parsing + * (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); - if (!empty($input)) { - $this->parse($input); + if (!empty($data)) { + $this->parse($data); } } /** - * @param integer|null $offset Number of rows to ignore from the + * @param int|null $offset Number of rows to ignore from the * beginning of the data - * @param integer|null $limit Limits the number of returned rows + * @param int|null $limit Limits the number of returned rows * to specified amount - * @param string|null $conditions Basic SQL-like conditions for row + * @param string|null $conditions Basic SQL-like conditions for row * matching - * @param null|true $keep_file_data Keep raw file data in memory after + * @param null|true $keep_file_data Keep raw file data in memory after * successful parsing * (useful for debugging) */ @@ -352,36 +355,45 @@ public function init($offset = null, $limit = null, $conditions = null, $keep_fi // ============================================== /** - * Parse * Parse a CSV file or string * - * @param string|null $input The CSV string or a direct file path - * @param integer $offset Number of rows to ignore from the + * @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 * beginning of the data - * @param integer $limit Limits the number of returned rows to + * @param int|null $limit Limits the number of returned rows to * specified amount - * @param string $conditions Basic SQL-like conditions for row + * @param string|null $conditions Basic SQL-like conditions for row * matching * * @return bool True on success */ - public function parse($input = null, $offset = null, $limit = null, $conditions = null) { - if (is_null($input)) { - $input = $this->file; + public function parse($dataString = null, $offset = null, $limit = null, $conditions = null) { + if (is_null($dataString)) { + $this->data = $this->parseFile(); + return $this->data !== false; } - if (empty($input)) { + if (empty($dataString)) { return false; } $this->init($offset, $limit, $conditions); - if (strlen($input) <= PHP_MAXPATHLEN && is_readable($input)) { - $this->file = $input; - $this->data = $this->_parse_file(); + if (strlen($dataString) <= PHP_MAXPATHLEN && is_readable($dataString)) { + $this->file = $dataString; + $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 { $this->file = null; - $this->file_data = &$input; + $this->file_data = &$dataString; $this->data = $this->_parse_string(); } @@ -389,17 +401,17 @@ public function parse($input = null, $offset = null, $limit = null, $conditions } /** - * 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 array $data 2D array of data - * @param bool $append Append current data to end of target CSV, if file + * @param string $file File location to save to + * @param array $data 2D array of data + * @param bool $append Append current data to end of target CSV, if file * exists - * @param array $fields Field names. Sets the header. If it is not set + * @param array $fields Field names. Sets the header. If it is not set * $this->titles would be used instead. * * @return bool + * True on success */ public function save($file = '', $data = array(), $append = FileProcessingModeEnum::MODE_FILE_OVERWRITE, $fields = array()) { if (empty($file)) { @@ -407,25 +419,26 @@ public function save($file = '', $data = array(), $append = FileProcessingModeEn } $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); } /** - * Output - * Generate a CSV based string for output. + * Generate a CSV-based string for output. * - * @param string|null $filename If a filename is specified here or in the + * Useful for exports in web applications. + * + * @param string|null $filename If a filename is specified here or in the * object, headers and data will be output * directly to browser as a downloadable * file. This file doesn't have to exist on * the server; the parameter only affects * how the download is called to the * browser. - * @param array[] $data 2D array with data - * @param array $fields Field names - * @param string|null $delimiter character used to separate data + * @param array[] $data 2D array with data + * @param array $fields Field names + * @param string|null $delimiter character used to separate data * * @return string The resulting CSV string */ @@ -458,11 +471,17 @@ public function output($filename = null, $data = array(), $fields = array(), $de } /** - * Encoding * Convert character encoding * - * @param string $input Input character encoding, uses default if left blank - * @param string $output Output 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 + * If the value null is passed, the existing input encoding remains set (default: ISO-8859-1). + * + * @return void */ public function encoding($input = null, $output = null) { $this->convert_encoding = true; @@ -476,17 +495,19 @@ public function encoding($input = null, $output = null) { } /** - * 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 * - * @param string|null $file Local CSV file - * @param bool $parse True/false parse file directly - * @param int $search_depth Number of rows to analyze - * @param string $preferred Preferred delimiter characters - * @param string|null $enclosure Enclosure character, default is double quote ("). + * @param string|null $file Local CSV file + * Supplying CSV data (file content) here is deprecated. + * For CSV data, please use autoDetectionForDataString(). + * Support for CSV data will be removed in v2.0.0. + * @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 The detected field delimiter + * @return string|false The detected field delimiter */ public function auto($file = null, $parse = true, $search_depth = null, $preferred = null, $enclosure = null) { if (is_null($file)) { @@ -517,6 +538,13 @@ public function auto($file = null, $parse = true, $search_depth = null, $preferr $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)) { $this->_guess_delimiter($search_depth, $preferred, $enclosure, $data); } @@ -559,12 +587,10 @@ public function getTotalDataRowCount() { $headingRow = $this->heading ? 1 : 0; - $count = substr_count($data, "\r") + return substr_count($data, "\r") + substr_count($data, "\n") - substr_count($data, "\r\n") - $headingRow; - - return $count; } // ============================================== @@ -572,23 +598,33 @@ public function getTotalDataRowCount() { // ============================================== /** - * Parse File * 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|false */ - protected function _parse_file($file = null) { + public function parseFile($file = null) { if (is_null($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(); } /** @@ -600,9 +636,10 @@ protected function _parse_file($file = null) { * * To detect field separators, please use auto() instead. * - * @param string $data CSV data + * @param string|null $data CSV data * - * @return array|false - 2D array with CSV data, or false on failure + * @return array|false + * 2D array with CSV data, or false on failure */ protected function _parse_string($data = null) { if (empty($data)) { @@ -627,7 +664,7 @@ protected function _parse_string($data = null) { // force the parser to process end of data as a character (false) when // data does not end with a line feed or carriage return character. - $lch = $data{$strlen - 1}; + $lch = $data[$strlen - 1]; if ($lch != "\n" && $lch != "\r") { $data .= "\n"; $strlen++; @@ -635,8 +672,8 @@ protected function _parse_string($data = null) { // walk through each character for ($i = 0; $i < $strlen; $i++) { - $ch = isset($data{$i}) ? $data{$i} : false; - $nch = isset($data{$i + 1}) ? $data{$i + 1} : false; + $ch = isset($data[$i]) ? $data[$i] : false; + $nch = isset($data[$i + 1]) ? $data[$i + 1] : false; // open/close quotes, and inline quotes if ($ch == $this->enclosure) { @@ -666,10 +703,10 @@ protected function _parse_string($data = null) { $i++; } elseif ($nch != $this->delimiter && $nch != "\r" && $nch != "\n") { $x = $i + 1; - while (isset($data{$x}) && ltrim($data{$x}, $white_spaces) == '') { + while (isset($data[$x]) && ltrim($data[$x], $white_spaces) == '') { $x++; } - if ($data{$x} == $this->delimiter) { + if ($data[$x] == $this->delimiter) { $enclosed = false; $i = $x; } else { @@ -699,9 +736,8 @@ protected function _parse_string($data = null) { } else { $enclosed = false; } - // 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; $row[$key] = $was_enclosed ? $current : trim($current); $current = ''; @@ -709,7 +745,7 @@ protected function _parse_string($data = null) { $col++; // end of row - if ($ch == "\n" || $ch == "\r" || $ch === false) { + if (in_array($ch, ["\n", "\r", false], true)) { if ($this->_validate_offset($row_count) && $this->_validate_row_conditions($row, $this->conditions)) { if ($this->heading && empty($head)) { $head = $row; @@ -819,9 +855,8 @@ public function unparse($data = array(), $fields = array(), $append = FileProces $string .= implode($delimiter, $entry) . $this->linefeed; $entry = array(); } - // create data - foreach ($data as $key => $row) { + foreach ($data as $row) { foreach (array_keys($fieldOrder) as $index) { $cell_value = $row[$index]; $entry[] = $this->_enclose_value($cell_value, $delimiter); @@ -832,13 +867,28 @@ public function unparse($data = array(), $fields = array(), $append = FileProces } if ($this->convert_encoding) { - $string = iconv($this->input_encoding, $this->output_encoding, $string); + /** @noinspection PhpComposerExtensionStubsInspection + * + * If you receive an error at the following 3 lines, you must enable + * the following PHP extension: + * + * - if $use_mb_convert_encoding is true: mbstring + * - if $use_mb_convert_encoding is false: iconv + */ + $string = $this->use_mb_convert_encoding ? + mb_convert_encoding($string, $this->output_encoding, $this->input_encoding) : + iconv($this->input_encoding, $this->output_encoding, $string); } return $string; } - private function _validate_fields_for_unparse($fields) { + /** + * @param array $fields + * + * @return array|false + */ + private function _validate_fields_for_unparse(array $fields) { if (empty($fields)) { $fields = $this->titles; } @@ -886,32 +936,71 @@ private function _validate_fields_for_unparse($fields) { * This function load_data() is able to handle BOMs and encodings. The data * 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) { - $data = null; - $file = null; + return $this->loadFile($input); + } - if (is_null($input)) { - $file = $this->file; - } elseif (\strlen($input) <= PHP_MAXPATHLEN && file_exists($input)) { - $file = $input; - } else { - // It is CSV data as a string. - $data = $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 + */ + public function loadFile($file = null) { + $data = null; - if (!empty($data) || $data = $this->_rfile($file)) { + if (is_null($file)) { + $data = $this->_rfile($this->file); + } elseif (\strlen($file) <= PHP_MAXPATHLEN && file_exists($file)) { + $data = $this->_rfile($file); if ($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)) { - $data = ltrim($strip[1]); - } + // WARNING: + // 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) { // strip off BOM (UTF-8) $data = substr($data, 3); @@ -927,6 +1016,14 @@ public function load_data($input = null) { } if ($this->convert_encoding && $this->input_encoding !== $this->output_encoding) { + /** @noinspection PhpComposerExtensionStubsInspection + * + * If you receive an error at the following 3 lines, you must enable + * the following PHP extension: + * + * - if $use_mb_convert_encoding is true: mbstring + * - if $use_mb_convert_encoding is false: iconv + */ $data = $this->use_mb_convert_encoding ? mb_convert_encoding($data, $this->output_encoding, $this->input_encoding) : iconv($this->input_encoding, $this->output_encoding, $data); @@ -953,7 +1050,7 @@ public function load_data($input = null) { * @param array $row array with values from a row * @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) { if (!empty($row)) { @@ -1011,6 +1108,8 @@ protected function _validate_row_condition($row, $condition) { 'is greater than or equals', 'contains', 'does not contain', + 'is number', + 'is not number', ); $operators_regex = array(); @@ -1023,10 +1122,22 @@ protected function _validate_row_condition($row, $condition) { if (preg_match('/^(.+) (' . $operators_regex . ') (.+)$/i', trim($condition), $capture)) { $field = $capture[1]; - $op = $capture[2]; + $op = strtolower($capture[2]); $value = $capture[3]; + if ($op == 'equals' && preg_match('/^(.+) is (less|greater) than or$/i', $field, $m)) { + $field = $m[1]; + $op = strtolower($m[2]) == 'less' ? '<=' : '>='; + } + if ($op == 'is' && preg_match('/^(less|greater) than (.+)$/i', $value, $m)) { + $value = $m[2]; + $op = strtolower($m[1]) == 'less' ? '<' : '>'; + } + if ($op == 'is' && preg_match('/^not (.+)$/i', $value, $m)) { + $value = $m[1]; + $op = '!='; + } - if (preg_match('/^([\'\"]{1})(.*)([\'\"]{1})$/', $value, $capture) && $capture[1] == $capture[3]) { + if (preg_match('/^([\'"])(.*)([\'"])$/', $value, $capture) && $capture[1] == $capture[3]) { $value = strtr($capture[2], array( "\\n" => "\n", "\\r" => "\r", @@ -1037,7 +1148,12 @@ protected function _validate_row_condition($row, $condition) { } if (array_key_exists($field, $row)) { - if (($op == '=' || $op == 'equals' || $op == 'is') && $row[$field] == $value) { + $op_equals = in_array($op, ['=', 'equals', 'is'], true); + if ($op_equals && $row[$field] == $value) { + 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) { return '1'; @@ -1067,7 +1183,7 @@ protected function _validate_row_condition($row, $condition) { * * @param int $current_row the current row number being processed * - * @return true of false + * @return bool */ protected function _validate_offset($current_row) { return @@ -1093,7 +1209,7 @@ protected function _enclose_value($value, $delimiter) { : ''; $enclosure_quoted = preg_quote($this->enclosure, '/'); $pattern = "/" . $delimiter_quoted . $enclosure_quoted . "|\n|\r/i"; - if ($this->enclose_all || preg_match($pattern, $value) || ($value{0} == ' ' || substr($value, -1) == ' ')) { + if ($this->enclose_all || preg_match($pattern, $value) || strpos($value, ' ') === 0 || substr($value, -1) == ' ') { $value = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $value); $value = $this->enclosure . $value . $this->enclosure; } @@ -1105,7 +1221,7 @@ protected function _enclose_value($value, $delimiter) { /** * Check file data * - * @param string|null $file local filename + * @param string|null $file local filename * * @return bool */ @@ -1115,20 +1231,20 @@ protected function _check_data($file = null) { $file = $this->file; } - return $this->load_data($file); + return $this->loadFile($file); } return true; } /** - * Check if passed info might be delimiter + * Check if passed info might be delimiter. * Only used by find_delimiter * - * @param string $char Potential field separating character - * @param array $array Frequency - * @param int $depth Number of analyzed rows - * @param string $preferred Preferred delimiter characters + * @param string $char Potential field separating character + * @param array $array Frequency + * @param int $depth Number of analyzed rows + * @param string $preferred Preferred delimiter characters * * @return string|false special string used for delimiter selection, or false */ @@ -1137,7 +1253,7 @@ protected function _check_count($char, $array, $depth, $preferred) { $first = null; $equal = null; $almost = false; - foreach ($array as $key => $value) { + foreach ($array as $value) { if ($first == null) { $first = $value; } elseif ($value == $first && $equal !== false) { @@ -1150,7 +1266,7 @@ protected function _check_count($char, $array, $depth, $preferred) { } } - if ($equal) { + if ($equal || $depth === 1) { $match = $almost ? 2 : 1; $pref = strpos($preferred, $char); $pref = ($pref !== false) ? str_pad($pref, 3, '0', STR_PAD_LEFT) : '999'; @@ -1166,16 +1282,23 @@ protected function _check_count($char, $array, $depth, $preferred) { /** * Read local file. * - * @param string $file local filename + * @param string $filePath local filename * * @return string|false Data from file, or false on failure */ - protected function _rfile($file) { - if (is_readable($file)) { - $data = file_get_contents($file); + protected function _rfile($filePath) { + if (is_readable($filePath)) { + $data = file_get_contents($filePath); if ($data === 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"); } @@ -1185,12 +1308,14 @@ protected function _rfile($file) { /** * Write to local file * - * @param string $file local filename - * @param string $content data to write to file - * @param string $mode fopen() mode - * @param int $lock flock() mode + * @param string $file local filename + * @param string $content data to write to file + * @param string $mode fopen() mode + * @param int $lock flock() mode + * + * @return bool + * True on success * - * @return true or false */ protected function _wfile($file, $content = '', $mode = 'wb', $lock = LOCK_EX) { if ($fp = fopen($file, $mode)) { @@ -1252,7 +1377,6 @@ protected function _detect_and_remove_sep_row_from_data(&$data_string) { } // remove delimiter and its line-end (the data param is by-ref!) - /** @noinspection CallableParameterUseCaseInTypeContextInspection */ $data_string = substr($data_string, $pos); return true; } @@ -1263,7 +1387,7 @@ protected function _detect_and_remove_sep_row_from_data(&$data_string) { * @param string $enclosure Enclosure character, default is double quote * @param string $data The file content */ - protected function _guess_delimiter($search_depth, $preferred, $enclosure, &$data) { + protected function _guess_delimiter($search_depth, $preferred, $enclosure, $data) { $chars = []; $strlen = strlen($data); $enclosed = false; @@ -1278,15 +1402,15 @@ protected function _guess_delimiter($search_depth, $preferred, $enclosure, &$dat // walk specific depth finding possible delimiter characters for ($i = 0; $i < $strlen; $i++) { - $ch = $data{$i}; - $nch = isset($data{$i + 1}) ? $data{$i + 1} : false; - $pch = isset($data{$i - 1}) ? $data{$i - 1} : false; + $ch = $data[$i]; + $nch = isset($data[$i + 1]) ? $data[$i + 1] : false; + $pch = isset($data[$i - 1]) ? $data[$i - 1] : false; // open and closing quotes $is_newline = ($ch == "\n" && $pch != "\r") || $ch == "\r"; if ($ch == $enclosure) { if (!$enclosed || $nch != $enclosure) { - $enclosed = $enclosed ? false : true; + $enclosed = !$enclosed; } elseif ($enclosed) { $i++; } diff --git a/lib/parsecsv/src/enums/SortEnum.php b/lib/parsecsv/src/enums/SortEnum.php index fd1cb1e..5d50e79 100644 --- a/lib/parsecsv/src/enums/SortEnum.php +++ b/lib/parsecsv/src/enums/SortEnum.php @@ -26,5 +26,4 @@ public static function getSorting($type) { return self::$sorting[self::__DEFAULT]; } - } diff --git a/lib/parsecsv/src/extensions/DatatypeTrait.php b/lib/parsecsv/src/extensions/DatatypeTrait.php index 5b22935..c0a4c10 100644 --- a/lib/parsecsv/src/extensions/DatatypeTrait.php +++ b/lib/parsecsv/src/extensions/DatatypeTrait.php @@ -19,7 +19,7 @@ trait DatatypeTrait { * Check data type for one column. * Check for most commonly data type for one column. * - * @param array $datatypes + * @param array $datatypes * * @return string|false */ @@ -36,7 +36,6 @@ private function getMostFrequentDatatypeForColumn($datatypes) { reset($typesFreq); return key($typesFreq); - } /** diff --git a/views/export.php b/views/export.php index bc2a4fa..2a533b8 100644 --- a/views/export.php +++ b/views/export.php @@ -30,6 +30,10 @@ +
  • + + +