Lo bueno que tienen las hojas de cálculo es que las puedes hacer con un html sencillo de <tables> y el resultado lo pintas diciéndole al navegador las cabeceras adecuadas, indicando que es un fichero con el mimetype spreadsheet.
La otra ventaja es que existen librerías u objetos preparados para el tema.
Échale un vistazo a esto:
https://phpexcel.codeplex.com/ https://github.com/PHPOffice/PHPExcel
Por otro lado, un ejemplo de código que tengo hecho de hace varios años y sigue funcionando:
Código PHP:
Ver originalheader("Content-type: application/vnd.ms-excel; charset=iso-8859-1"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Content-Disposition: attachment; filename=Informe_".date("Y-m-d",time()).".xls"); header("Content-Transfer-Encoding: binary"); $html = <<<EOH
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv=Content-Type content="text/html; charset=iso-8859-1">
<meta name=ProgId content=Excel.Sheet>
<!--[if gte mso 9]><xml>
<o:DocumentProperties>
<o:LastAuthor>Sriram</o:LastAuthor>
<o:LastSaved>2005-01-02T07:46:23Z</o:LastSaved>
<o:Version>10.2625</o:Version>
</o:DocumentProperties>
<o:OfficeDocumentSettings>
<o:DownloadComponents/>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<style>
<!--table
{mso-displayed-decimal-separator:"\.";
mso-displayed-thousand-separator:"\,";}
@page
{margin:1.0in .75in 1.0in .75in;
mso-header-margin:.5in;
mso-footer-margin:.5in;}
tr
{mso-height-source:auto;}
col
{mso-width-source:auto;}
br
{mso-data-placement:same-cell;}
.style0
{mso-number-format:General;
text-align:general;
vertical-align:bottom;
white-space:nowrap;
mso-rotate:0;
mso-background-source:auto;
mso-pattern:auto;
color:windowtext;
font-size:10.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:Arial;
mso-generic-font-family:auto;
mso-font-charset:0;
border:none;
mso-protection:locked visible;
mso-style-name:Normal;
mso-style-id:0;}
td
{mso-style-parent:style0;
padding-top:1px;
padding-right:1px;
padding-left:1px;
width:200px;
mso-ignore:padding;
color:windowtext;
font-size:10.0pt;
font-weight:400;
font-style:normal;
text-decoration:none;
font-family:Arial;
mso-generic-font-family:auto;
mso-font-charset:0;
mso-number-format:General;
text-align:general;
vertical-align:bottom;
border:none;
mso-background-source:auto;
mso-pattern:auto;
mso-protection:locked visible;
white-space:nowrap;
mso-rotate:0;}
.xl24
{mso-style-parent:style0;
white-space:normal;}
th
{text-align:center;
font-weight:bold;}
-->
</style>
<!--[if gte mso 9]><xml>
<x:ExcelWorkbook>
<x:ExcelWorksheets>
<x:ExcelWorksheet>
<x:Name>srirmam</x:Name>
<x:WorksheetOptions>
<x:Selected/>
<x:ProtectContents>False</x:ProtectContents>
<x:ProtectObjects>False</x:ProtectObjects>
<x:ProtectScenarios>False</x:ProtectScenarios>
</x:WorksheetOptions>
</x:ExcelWorksheet>
</x:ExcelWorksheets>
<x:WindowHeight>20005</x:WindowHeight>
<x:WindowWidth>20005</x:WindowWidth>
<x:WindowTopX>120</x:WindowTopX>
<x:WindowTopY>135</x:WindowTopY>
<x:ProtectStructure>False</x:ProtectStructure>
<x:ProtectWindows>False</x:ProtectWindows>
</x:ExcelWorkbook>
</xml><![endif]-->
</head>
<body link=blue vlink=purple>
<table x:str border=0 cellpadding=0 cellspacing=0 style='border-collapse: collapse;table-layout:fixed; font-family: Arial'>
EOH;
switch($this->Action) {
case "getReportByUser":
//Informe individual por usuario
$html.= $this->getReportByUser($idUser, 1);
break;
default :
//Error si no se encuentra la acción
$html.=$this->Error();
break;
}
$html.="</table></body></html>";
return $html;
}
private function getReportByUser($user, $type) {
return "<tr><td>Col 1 Row 1</td><td>Col 2 Row 1</td></tr><tr><td>Col 1 Row 2</td><td>Col 2 Row 2</td></tr>";
}
Espero te sea útil la información.
Saludos.