CSV Helper (PHP 5)

By Adam (ifunk)
I was recently outputting some comma-delimited data and thought I would benefit from a simple csv helper. I hope this helper benefits someone else as well. This is my first submission so please give any criticism.

This CSV helper enables you to output data in a csv format. It can automatically output the necessary headers so that it will open in Microsoft Excel.

Sample Usage

Download code $line = array('First Name', 'Last Name', 'Gender', 'City');
$csv->addRow($line);

$line = array('Adam', 'Royle', 'M', 'Brisbane');
$csv->addRow($line);

$line = array('Skrimpy', 'Bopimpy', 'M', 'North Sydney');
$csv->addRow($line);

$line = array('Sarah', 'Jincera"s', 'F', 'Melbourne');
$csv->addRow($line);

echo $csv->render('Subscribers.csv'); 

This will output the following:

Download code "First Name","Last Name",Gender,City
Adam,Royle,M,Brisbane
Skrimpy,Bopimpy,M,"North Sydney"
Sarah,"Jincera""s",F,Melbourne

Instead of the addRow() method, you can also add one field at a time.

Download code $csv->addField('Adam');
$csv->addField('Royle');
$csv->addField('M');
$csv->addField('Brisbane');
$csv->endRow();

If you don't want to render headers simply use:

Download code $csv->render(false);

Save the following file as csv.php into your app/views/helpers/ directory, and include 'Csv' in your $helpers array in your controller.

Helper Class:

Download code <?php 
 
class CsvHelper extends AppHelper {
    
    var 
$delimiter ',';
    var 
$enclosure '"';
    var 
$filename 'Export.csv';
    var 
$line = array();
    var 
$buffer;
    
    function 
CsvHelper() {
        
$this->clear();
    }
    
    function 
clear() {
        
$this->line = array();
        
$this->buffer fopen('php://temp/maxmemory:'. (5*1024*1024), 'r+');
    }
    
    function 
addField($value) {
        
$this->line[] = $value;
    }
    
    function 
endRow() {
        
$this->addRow($this->line);
        
$this->line = array();
    }
    
    function 
addRow($row) {
        
fputcsv($this->buffer$row$this->delimiter$this->enclosure);
    }
    
    function 
renderHeaders() {
        
header("Content-type:application/vnd.ms-excel");
        
header("Content-disposition:attachment;filename=".$this->filename);
    }
    
    function 
setFilename($filename) {
        
$this->filename $filename;
        if (
strtolower(substr($this->filename, -4)) != '.csv') {
            
$this->filename .= '.csv';
        }
    }
    
    function 
render($outputHeaders true$to_encoding null$from_encoding "auto") {
        if (
$outputHeaders) {
            if (
is_string($outputHeaders)) {
                
$this->setFilename($outputHeaders);
            }
            
$this->renderHeaders();
        }
        
rewind($this->buffer);
        
$output stream_get_contents($this->buffer);
        if (
$to_encoding) {
            
$output mb_convert_encoding($output$to_encoding$from_encoding);
        }
        return 
$this->output($output);
    }
}

?>

 

Comments 527

CakePHP Team Comments Author Comments
 

Comment

1 optional iconv call

Nice well done, just a potential modification (I need to deal with it sometimes).
When you have real UTF-8 language, you can come up with a CSV that is not readable under windows (ie : majority of people use excel for viewing csv). So maybe 2 parameters for an optional call to iconv or mb_convert in order to convert the encoding (ie UTF-8 CP1251 -> Cyrillic for example)...
Posted Sep 10, 2007 by franck
 

Comment

2 Re optional iconv call

Thanks for your comments franck. I've never had to use UTF-8 so I've never run into these issues. I've made some modifications to the render() method. Let me know if this looks good to you or if there is anything you would change.

Posted Sep 10, 2007 by Adam
 

Question

3 Where did you find information about cake extract

when I start with Multilang in Cake 1.2 in feb 2007 in Google group I spoke with some of the project owners and they said that parser for __() content is not available. Then I wrote one my self -quick and dirty, of course which I'm using still. Unfortunately I never find an article how to use cake extract and when cake extract appear in the cake script. Recently I notice that there is extract option, but I thought it's something for extracting files or similar. I will give a try with it other than that - I am using the same approaches which you describe in that great article.
Posted Sep 11, 2007 by Nik Chankov
 

Bug

4 Warning fopen

Warning: fopen(php://temp/maxmemory:5242880): failed to open stream: Success in

I get this. I am using PHP 4. Like the idea of it. Plan on using it. Do you have any help?
Posted Sep 14, 2007 by Christian Pearce
 

Comment

5 Re warning fopen

Warning: fopen(php://temp/maxmemory:5242880): failed to open stream: Success in

I get this. I am using PHP 4. Like the idea of it. Plan on using it. Do you have any help?

Hi Christian, yes the above code will only work in PHP 5 (as mentioned in the title). It utilises a few PHP5 functions such as fputcsv(), fopen() in temp memory and stream_get_contents().

I don't plan on converting it to PHP4 as I no longer use it, but will consider if enough people want me to. So cast your votes people.
Posted Sep 14, 2007 by Adam
 

Comment

6 PHP4 compatibility

I have added PHP4 and CakePHP 1.1.x.x compatibility on my dev box for this class, and also added some more features. I will post as soon as I refine my method/variable names.
Posted Sep 15, 2007 by Adam
 

Comment

7 Test it

I have added PHP4 and CakePHP 1.1.x.x compatibility on my dev box for this class, and also added some more features. I will post as soon as I refine my method/variable names.
I have to release something Monday. I wouldn't mind testing it now if you post it. christian at pearcec dot com
Posted Sep 16, 2007 by Christian Pearce
 

Comment

8 Undefined variable csv

Notice: Undefined variable: csv in /home/rwkelly/public_html/cake/app/controllers/users_controller.php on line 174

Fatal error: Call to a member function addField() on a non-object in /home/rwkelly/public_html/cake/app/controllers/users_controller.php on line 174

I'm getting these errors when I follow this tutorial. I added 'Csv' to the $helpers array. I added the helper file to the proper place. I also tried setting $csv = new CsvHelper(); but that didn't help at all. What have I missed?
Posted Sep 18, 2007 by Richard Savage
 

Comment

9 Re undefined variable csv

I'm getting these errors when I follow this tutorial. I added 'Csv' to the $helpers array. I added the helper file to the proper place. I also tried setting $csv = new CsvHelper(); but that didn't help at all. What have I missed?
Hi Richard, you need to use this helper from one of your views.
Posted Sep 18, 2007 by Adam
 

Comment

10 Re PHP4 compatibility

I have added PHP4 and CakePHP 1.1.x.x compatibility on my dev box for this class, and also added some more features. I will post as soon as I refine my method/variable names.
Any news on this? I have a 1.1 project that I need to deploy on a server with PHP4. This class would be really handy for it.
Posted Oct 23, 2007 by Daniel McCallum
 

Comment

11 PHP4 Compatibility

Any news on this? I have a 1.1 project that I need to deploy on a server with PHP4. This class would be really handy for it.
Please see my updated class below. There is a new method called addGrid() which accepts data in the same format as returned by $model->findAll(), automatically adding field names as the first row (this behaviour can be overridden in the second argument).

http://ifunk.net/cakephp/helpers/csv.php.txt
Here is an example (obviously you need an "orders" table and an Order model for this to work):

Controller Class:

<?php class OrdersController extends AppController {
    function 
view() {
        
$this->set('data'$this->Order->findAll());
    }
}
?>

View Template:

<?php
$csv
->addGrid($data);
echo 
$csv->render();
?>

Would be great if I could get some feedback on this class. Once it is more tested I will write up and post a detailed tutorial, with info on how to attach to an email using phpmailer, etc.

Also, you may notice that Excel automatically removes zeroes from the beginning of a numeric string. I have added in known workaround for this behaviour, but it only works in Excel so it should be used only when needed. Eg.

$csv->preserveLeadingZerosInExcel = true;
Posted Oct 23, 2007 by Adam
 

Comment

12 Still active question mark

looks like this thread hasn't been updated in a while... nonetheless, i just stumbled upon this great helper. I was trying to write similar functionality into my app, and this has done most of the trick so far.

It would be even more awesome if we could figure out a way to include associated model data into the report as well - e.g. for Orders: Product info, Customer info, etc.

Obviously there are many potential pitfalls, but I think it could be done well.

Let me know if anyone is interested or if someone has begun this already.
Posted Feb 13, 2008 by ambiguator
 

Comment

13 I rewrote this for PHP4

I had to make this work with PHP4 for a current project, in case anyone is interested. I took out the encoding stuff and php5 tempfile buffer, and replaced fputcsv with something else. It just buffers it to a string, then dumps it at the end. Works well for what I am doing.

Helper Class:

<?php 

class CsvHelper extends AppHelper {
    
    var 
$delimiter ',';
    var 
$enclosure '"';
    var 
$filename 'Export.csv';
    var 
$line = array();
    var 
$buffer;
    
    function 
CsvHelper() {
        
$this->clear();
    }
    
    function 
clear() {
        
$this->line = array();
        
$this->buffer '';
    }
    
    function 
makecsv($fields = array(), $delimiter ','$enclosure '"') {
        
$str '';
        
$escape_char '\\';
        foreach (
$fields as $value) {
          if (
strpos($value$delimiter) !== false ||
              
strpos($value$enclosure) !== false ||
              
strpos($value"\n") !== false ||
              
strpos($value"\r") !== false ||
              
strpos($value"\t") !== false ||
              
strpos($value' ') !== false) {
            
$str2 $enclosure;
            
$escaped 0;
            
$len strlen($value);
            for (
$i=0;$i<$len;$i++) {
              if (
$value[$i] == $escape_char) {
                
$escaped 1;
              } else if (!
$escaped && $value[$i] == $enclosure) {
                
$str2 .= $enclosure;
              } else {
                
$escaped 0;
              }
              
$str2 .= $value[$i];
            }
            
$str2 .= $enclosure;
            
$str .= $str2.$delimiter;
          } else {
            
$str .= $value.$delimiter;
          }
        }
        
$str substr($str,0,-1);
        
$str .= "\n";
        return (
$str);
    }
    
    function 
addField($value) {
        
$this->line[] = $value;
    }
    
    function 
endRow() {
        
$this->addRow($this->line);
        
$this->line = array();
    }
    
    function 
addRow($row) {
        
$this->buffer .= $this->makecsv($row$this->delimiter$this->enclosure);
    }
    
    function 
renderHeaders() {
        
header("Content-type:application/vnd.ms-excel");
        
header("Content-disposition:attachment;filename=".$this->filename);
    }
    
    function 
setFilename($filename) {
        
$this->filename $filename;
        if (
strtolower(substr($this->filename, -4)) != '.csv') {
            
$this->filename .= '.csv';
        }
    }
    
    function 
render($outputHeaders true) {
        if (
$outputHeaders) {
            if (
is_string($outputHeaders)) {
                
$this->setFilename($outputHeaders);
            }
            
$this->renderHeaders();
        }
        return 
$this->output($this->buffer);
    }
}

?>
Posted Feb 21, 2008 by scook
 

Comment

14 PHP4

I had to make this work with PHP4 for a current project, in case anyone is interested. I took out the encoding stuff and php5 tempfile buffer, and replaced fputcsv with something else. It just buffers it to a string, then dumps it at the end. Works well for what I am doing.
I extended this helper a few days after I submitted the article with PHP4 compatibility and a few new features.

http://ifunk.net/cakephp/helpers/csv.php.txt
I will be writing a tutorial that will be placed on my new blog (http://blogs.bigfish.tv/adam/).
Posted Feb 21, 2008 by Adam
 

Comment

15 HTTP Headers

First of all, thanks for this code.

I have found just one bug (or flaw). In Opera it automagicallly renamed the file to have a .xls extension.

In order to fix this, I have changed the response header Content-Type to 'application/csv' (instead of 'application/vnd.ms-excel'), which is a more general mimetype declaration. It worked in IE7, Firefox 2 and Opera 9!


The code:
header("Content-Type: application/csv");
Posted Jun 5, 2008 by Mr. Xanadu
 

Comment

16 Getting the web page instead of the data in the csv file

I got Adam's stuff from above and have nearly an identical view file and findAll call. However, the csv file I get has the contents of the web page instead of the csv data. Anyone know why?
Posted Jul 3, 2008 by Charles White
 

Comment

17 Awesome

Thanks a lot for this helper. This makes generating spreadsheets so much easier. Using the latest version Adam posted on PHP4. Thanks for the rewrite!
Posted Jul 20, 2008 by Casey
 

Comment

18 trying to create the csv with a button help please

Well i am trying to create a link on the view/..../..thtml

in the thtml file i write just :

">click to execute csv

if ( isset($execute) ){
myfunction();
}
?>


and in the controller
function myfunction(){
echo $csv->render('Subscribers.csv');

$csv = $this->Csv->findAll();

$this->set(array(
'csv' => $csv

));
}


and getting :

Notice (8): Undefined variable: csv [APP\controllers\orders_controller.php, line 171]
Code





echo $csv->render('Subscribers.csv');

Fatal error: Call to a member function render() on a non-object in C:\Program Files\a\www\dmesystem\app\controllers\orders_controller.php on line 171


help me please

Posted Jul 30, 2008 by Vasiok
 

Comment

19 Just a small addition to allow for spaces in file names

First, nice helper. Very easy and stright forward to use. Thanks!

However, using the http://ifunk.net/cakephp/helpers/csv.php.txt code, you can't have files with spaces in the name when calling $this->csv->render().

Example
$this->csv->render( 'This is a great helper.csv' ).
So to fix this, I changed some code on my end. In the renderHeaders function, I change the line
header("Content-disposition:attachment;filename=".$this->filename);

to this
header( "Content-disposition:attachment;filename=\"{$this->filename}\"" );
Now you can have a file name like "This is a super.csv" :)
Posted Jul 30, 2008 by Terry
 

Comment

20 Getting the web page instead of the data in the csv file

I got Adam's stuff from above and have nearly an identical view file and findAll call. However, the csv file I get has the contents of the web page instead of the csv data. Anyone know why?
I have the same problem? Did you get a solution on this?
Posted Oct 2, 2008 by Thomas Heinrich
 

Comment

21 Getting the web page instead of the data in the csv file

I got Adam's stuff from above and have nearly an identical view file and findAll call. However, the csv file I get has the contents of the web page instead of the csv data. Anyone know why?
I have the same problem? Did you get a solution on this?

I found the solution on this in a Newsgroup article.

In the controller method you must add the following:

$this->layout = null;
$this->autoLayout = false;

Now the data are in the file not the HTML page.
Posted Oct 2, 2008 by Thomas Heinrich
 

Comment

22 Thanks! Thanks! Thanks!

Hi Adam, first wanna thank you for a wonderfull code.
I have tried many libreries and helps, but always with problems in exporting excel(CSV) files.
Your code is working and exporting datas into the CSV file, but always puts datas starting with 6-th line, putting 5 empty lines at the begining of the file.
Can you tell me why please?
Thanks a lot
Posted May 7, 2009 by Nelli
 

Comment

23 Check spaces in your php files

Your code is working and exporting datas into the CSV file, but always puts datas starting with 6-th line, putting 5 empty lines at the begining of the file.
Can you tell me why please?

I suspect you've got spaces/line-breaks outside of the php tags in your other php files, eg your models, views, etc. I don't think it would be the result of the csv helper.
Posted May 7, 2009 by Adam
 

Comment

24 problem with UTF8

Your code is working and exporting datas into the CSV file, but always puts datas starting with 6-th line, putting 5 empty lines at the begining of the file.
Can you tell me why please?

I suspect you've got spaces/line-breaks outside of the php tags in your other php files, eg your models, views, etc. I don't think it would be the result of the csv helper.

Yes, the problem was in it, thanks again!

Wanna ask you another question about UTF-8, in the database have a datas in UTF-8 and need export them to the .csv(utf-8):
In the view I´m putting this:
echo $csv->render('Subscribers.csv', 'UTF-8', mb_detect_encoding($line));

but in the CSV recieve caracters like iñan.
What can I do?
thanks
Posted May 7, 2009 by Nelli
 

Comment

25 Lovely bit of code

Worked beautifully, thank you.

First I had to figure out how to create a view that didn't display any code except for the csv.

To do this I added an export function to my controller and set the layout to be 'ajax' which just happens to contain no html outside the main content. It looked something like this:

class MyController extends AppController {
$helpers = array('HTML', 'Form', 'Csv');
...
function export($id = null) {
$this->layout = 'ajax';
...
}

}

Then you create an export.ctp view with the code
Posted Jun 18, 2009 by Ian Channing