├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Data.php └── Parser.php └── tests ├── DataTest.php ├── ParserTest.php └── files ├── no-first-column.csv └── users.csv /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | tests/coverage/ 3 | composer.lock 4 | .DS_Store 5 | clover.xml 6 | .phpunit.result.cache -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - 7.1 9 | 10 | before_script: 11 | - composer self-update 12 | - composer install --prefer-source --no-interaction --dev 13 | 14 | before_install: 15 | - pip install --user codecov 16 | 17 | after_success: 18 | - bash <(curl -s https://codecov.io/bash) 19 | 20 | script: phpunit 21 | 22 | matrix: 23 | include: 24 | - php: 5.3 25 | dist: precise 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.0.6 4 | 5 | September 4, 2024 6 | 7 | - Fix for "Passing null to parameter of type string is deprecated." [#14](https://github.com/jamesgordo/php-csv-parser/pull/14) 8 | - Fix for "Creation of dynamic property is deprecated." [(@Serzol64)](https://github.com/jamesgordo/php-csv-parser/pull/10) 9 | - Updated PHPUnit Version & Configuration and unit test implementation. [#12](https://github.com/jamesgordo/php-csv-parser/pull/12) 10 | 11 | ## 1.0.5 12 | 13 | July 24, 2023 14 | 15 | - Bug Fix for first Column empty Issue [#7](https://github.com/jamesgordo/php-csv-parser/issues/7) 16 | 17 | ## 1.0.1 18 | 19 | August 21, 2017 20 | 21 | - Added option to set delimiters. [(jalbrecht)](https://github.com/jamesgordo/php-csv-parser/issues/1) 22 | - Removed limit to max characters. [(jalbrecht)](https://github.com/jamesgordo/php-csv-parser/issues/1) 23 | - Added Trim to header titles. [(jalbrecht)](https://github.com/jamesgordo/php-csv-parser/issues/1) 24 | - Simplified Parsed Data Object key and value mapping. [(nyamsprod)](https://www.reddit.com/r/PHP/comments/6g6vga/easy_and_convenient_php_csv_file_parsing/dio6n84/) 25 | 26 | ## 1.0.0 27 | 28 | Jun 6, 2017 29 | 30 | - Initial Version 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 James Gordo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP CSV Parser 2 | 3 | [![Build Status](https://travis-ci.org/jamesgordo/php-csv-parser.svg?branch=master)](https://travis-ci.org/jamesgordo/php-csv-parser) [![codecov](https://codecov.io/gh/jamesgordo/php-csv-parser/branch/master/graph/badge.svg)](https://codecov.io/gh/jamesgordo/php-csv-parser) [![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)](https://github.com/jamesgordo/php-csv-parser) 4 | 5 | Turn your CSV files into readable and accessable Data Objects easily. This Library wraps the PHP's built-in 6 | `fgetcsv` function to provide you a hassle free CSV File parsing. 7 | 8 | Each row on your CSV file is dynamically transformed into Data Objects with keys set directly from the first 9 | row of your CSV file. 10 | 11 | ## PHP Version Support 12 | 13 | The library has been tested to work on PHP Versions >=5.3. 14 | 15 | ## How to Use 16 | 17 | Run the following command in your terminal 18 | 19 | ``` 20 | composer require jamesgordo/php-csv-parser 21 | ``` 22 | 23 | Or simply add this to your `composer.json` 24 | 25 | ```json 26 | { 27 | "require": { 28 | "jamesgordo/php-csv-parser": "1.0.0" 29 | } 30 | } 31 | ``` 32 | 33 | Then run 34 | 35 | ``` 36 | composer update 37 | ``` 38 | 39 | Create a Sample CSV File `users.csv` 40 | 41 | ```csv 42 | id,first_name,last_name 43 | 1,John,Doe 44 | 2,Eric,Smith 45 | 3,Mark,Cooper 46 | ``` 47 | 48 | Example Implementation 49 | 50 | ```php 51 | all() as $user) { 62 | echo "User Details: {$user->id} | {$user->first_name} {$user->last_name}"; 63 | } 64 | 65 | echo "Total Parsed: " . $users->count() . " Users"; 66 | 67 | ``` 68 | 69 | ## Options 70 | 71 | You can set the second as delimiter. The default delimiter is ",". 72 | 73 | ```php 74 | setCsv('/path/to/file.csv'); // Sets the File to be Parsed 91 | $users->getCsv(); // Returns the File to be Parsed 92 | $users->checkFile('/path/to/file.csv'); // Validates if File is a valid CSV File 93 | $users->parse(); // Triggers the Parsing of CSV file 94 | $users->all(); // Returns array of Data Objects parsed from the CSV file 95 | $users->count(); // Returns the total rows parsed from the CSV file 96 | ``` 97 | 98 | ## Version 99 | 100 | 1.0.6 101 | 102 | ## License 103 | 104 | MIT License 105 | 106 | Copyright (c) 2024 James Gordo 107 | 108 | Permission is hereby granted, free of charge, to any person obtaining a copy 109 | of this software and associated documentation files (the "Software"), to deal 110 | in the Software without restriction, including without limitation the rights 111 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 112 | copies of the Software, and to permit persons to whom the Software is 113 | furnished to do so, subject to the following conditions: 114 | 115 | The above copyright notice and this permission notice shall be included in all 116 | copies or substantial portions of the Software. 117 | 118 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 119 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 120 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 121 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 122 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 123 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 124 | SOFTWARE. 125 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jamesgordo/php-csv-parser", 3 | "version": "1.0.6", 4 | "description": "Easiest and Convenient Way to Parse CSV Files using PHP.", 5 | "type": "library", 6 | "keywords": ["php", "csv parser", "php csv parser"], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "James Gordo" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.3" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "JamesGordo\\CSV\\": "src" 19 | } 20 | }, 21 | "autoload-dev": { 22 | "psr-4": { 23 | "namespace JamesGordo\\CSV\\Tests\\": "tests" 24 | } 25 | }, 26 | "require-dev": { 27 | "phpunit/phpunit": "^11.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/ 10 | 11 | 12 | 13 | 14 | src 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Data.php: -------------------------------------------------------------------------------- 1 | {$name} = $value; 35 | } 36 | 37 | /** 38 | * Retrieves dynamically set 39 | * object properties 40 | * 41 | * @return any 42 | */ 43 | public function __get($name) 44 | { 45 | return $this->{$name}; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Parser.php: -------------------------------------------------------------------------------- 1 | 0) { 78 | // Set the File to be Parsed 79 | $this->setCsv($csv); 80 | 81 | // Set the Delimeter 82 | $this->setDelimeter($delimiter); 83 | 84 | // trigger the parsing 85 | $this->parse(); 86 | } 87 | } 88 | 89 | /** 90 | * Sets the CSV file to be parsed. 91 | * 92 | * @param string $csv 93 | * @return void 94 | */ 95 | public function setCsv($csv) 96 | { 97 | // Verify if the CSV File is Valid 98 | $this->checkFile($csv); 99 | 100 | $this->csv = $csv; 101 | } 102 | 103 | /** 104 | * Sets the delimiter for parsing the csv 105 | * 106 | * @param $string $delimiter 107 | * @throws InvalidArgumentException Delimiter is not valid. 108 | * @return void 109 | */ 110 | public function setDelimeter($delimiter) 111 | { 112 | // verify if parameter meets the contract 113 | if (in_array($delimiter, $this->valid_delimiters) !== true) { 114 | throw new \InvalidArgumentException('Delimiter is not valid.'); 115 | } 116 | 117 | $this->delimiter = $delimiter; 118 | } 119 | 120 | /** 121 | * Retrieves the file that was 122 | * parsed. 123 | * 124 | * @return $this->csv 125 | */ 126 | public function getCsv() 127 | { 128 | return $this->csv; 129 | } 130 | 131 | /** 132 | * Checks wether the CSV file is Valid. 133 | * 134 | * @param string $csv 135 | * @throws InvalidArgumentException Filename is not a valid string. 136 | * @throws InvalidArgumentException File $csv does not exist. 137 | * @throws InvalidArgumentException File is not a valid csv file. 138 | * @return bool 139 | */ 140 | public function checkFile($csv = null) 141 | { 142 | // set the file to be checked 143 | $file = ($csv === null) ? $this->getCsv() : $csv; 144 | 145 | // verify if parameter meets the contract 146 | if (strlen($file) < 1) { 147 | throw new \InvalidArgumentException('Filename is not a valid string.'); 148 | } 149 | 150 | // verify if parameter meets the contract 151 | if (file_exists($file) !== true) { 152 | throw new \InvalidArgumentException("File {$file} does not exist."); 153 | } 154 | 155 | // verify if file is a valid csv file 156 | if (!is_object($file) && in_array(mime_content_type($file), $this->valid_mime_types) !== true) { 157 | throw new \InvalidArgumentException("File is not a valid csv file."); 158 | } 159 | 160 | return true; 161 | } 162 | 163 | /** 164 | * Parses the CSV file 165 | * 166 | * @return void 167 | */ 168 | public function parse() 169 | { 170 | // Verify if the CSV File is Valid 171 | $this->checkFile(); 172 | 173 | // parse the csv 174 | $items = array_map('str_getcsv', file($this->getCsv())); 175 | 176 | // Set the CSV Header 177 | $headers = array_shift($items); 178 | $this->headers = array_map(array($this,"_sanitize"), $headers); 179 | $this->data = array_map(array($this, 'mapHeaderToRow'), $items); 180 | } 181 | 182 | public function mapHeaderToRow($row) 183 | { 184 | return (object) array_combine($this->headers, $row); 185 | } 186 | 187 | /** 188 | * Retrieves all the CSV Data as array 189 | * 190 | * @return array stdClass 191 | */ 192 | public function all() 193 | { 194 | return $this->data; 195 | } 196 | 197 | /** 198 | * Returns the total number of rows 199 | * created into Data Object from the 200 | * parsed CSV file 201 | * 202 | * @return int 203 | */ 204 | public function count() 205 | { 206 | return count($this->data); 207 | } 208 | 209 | /** 210 | * Cleans Up Unwanted Characters 211 | * 212 | * @return string 213 | */ 214 | public function _sanitize($string) 215 | { 216 | return preg_replace("/\xEF\xBB\xBF/", "", trim($string)); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /tests/DataTest.php: -------------------------------------------------------------------------------- 1 | expectException('InvalidArgumentException'); 18 | $this->expectExceptionMessage('Parameter name must be a valid string.'); 19 | $data = new Data(); 20 | $data->__set('', 'value'); 21 | } 22 | 23 | /** 24 | * Testing the Magic Setter and Getter 25 | * 26 | * @return void 27 | */ 28 | public function testMagicSetGet() 29 | { 30 | $data = new Data(); 31 | $property = 'full_name'; 32 | $value = 'John Doe'; 33 | // set the value 34 | $data->{$property} = $value; 35 | 36 | // Verify the Value 37 | $this->assertEquals($value, $data->__get($property)); 38 | } 39 | } -------------------------------------------------------------------------------- /tests/ParserTest.php: -------------------------------------------------------------------------------- 1 | expectException('InvalidArgumentException'); 20 | $this->expectExceptionMessage("File $file does not exist"); 21 | 22 | // Parse the File 23 | $users = new Parser($file); 24 | } 25 | 26 | /** 27 | * Testing the Event of Parsing CSV with invalid delimiter 28 | * 29 | */ 30 | public function testInvalidDelimiter() 31 | { 32 | $this->expectException('InvalidArgumentException'); 33 | $this->expectExceptionMessage('Delimiter is not valid.'); 34 | 35 | // Set the file to be parsed 36 | $file = __DIR__ . '/files/users.csv'; 37 | 38 | // Parse the File 39 | $users = new Parser($file, '$'); 40 | } 41 | 42 | /** 43 | * Testing the Event of Setting empty filename 44 | * 45 | * @return void 46 | */ 47 | public function testSetEmptyFileName() 48 | { 49 | $this->expectException('InvalidArgumentException'); 50 | $this->expectExceptionMessage('Filename is not a valid string.'); 51 | 52 | // Initialize the Parser 53 | $users = new Parser(); 54 | $users->setCsv(''); 55 | $users->parse(); 56 | } 57 | 58 | /** 59 | * Test to verify the event of setting an invalid file type 60 | * 61 | */ 62 | public function testInvalidCsvFile() 63 | { 64 | $this->expectException('InvalidArgumentException'); 65 | $this->expectExceptionMessage('File is not a valid csv file.'); 66 | 67 | // set the invalid file 68 | $file = __DIR__ . '/DataTest.php'; 69 | 70 | // Initialize the Parser 71 | $users = new Parser($file); 72 | } 73 | 74 | /** 75 | * Test to Verify the CSV Parser 76 | * 77 | * @return void 78 | */ 79 | public function testParser() 80 | { 81 | // Expected Values 82 | $expected = array( 83 | array( 84 | 'id' => '1', 85 | 'first_name' => 'John', 86 | 'last_name' => 'Doe' 87 | ), 88 | array( 89 | 'id' => '2', 90 | 'first_name' => 'Eric', 91 | 'last_name' => 'Smith' 92 | ), 93 | array( 94 | 'id' => '3', 95 | 'first_name' => 'Mark', 96 | 'last_name' => 'Cooper' 97 | ) 98 | ); 99 | 100 | // Set the file to be parsed 101 | $csv = __DIR__ . '/files/users.csv'; 102 | 103 | // Parse the File 104 | $users = new Parser($csv); 105 | 106 | // Verify expected users values 107 | foreach($users->all() as $key => $user) { 108 | $this->assertEquals($expected[$key]['id'], $user->id); 109 | $this->assertEquals($expected[$key]['first_name'], $user->first_name); 110 | $this->assertEquals($expected[$key]['last_name'], $user->last_name); 111 | } 112 | 113 | } 114 | 115 | /** 116 | * Testing the Method to Count the total row Object 117 | * parsed from the file 118 | * 119 | * @return void 120 | */ 121 | public function testParseCount() 122 | { 123 | // Set the file to be parsed 124 | $csv = __DIR__ . '/files/users.csv'; 125 | 126 | // Parse the File 127 | $users = new Parser($csv); 128 | 129 | // Verify Expected Results 130 | $this->assertEquals(3, $users->count()); 131 | } 132 | 133 | public function testBlankFirstColumn() 134 | { 135 | // Set the file to be parsed 136 | $csv = __DIR__ . '/files/no-first-column.csv'; 137 | 138 | // Parse the File 139 | $users = new Parser($csv); 140 | 141 | foreach($users->all() as $key => $user) { 142 | $this->assertEquals(0, strlen($user->id)); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /tests/files/no-first-column.csv: -------------------------------------------------------------------------------- 1 | id,first_name,last_name 2 | ,John,Doe 3 | ,Eric,Smith 4 | ,Mark,Cooper -------------------------------------------------------------------------------- /tests/files/users.csv: -------------------------------------------------------------------------------- 1 | id,first_name,last_name 2 | 1,John,Doe 3 | 2,Eric,Smith 4 | 3,Mark,Cooper --------------------------------------------------------------------------------