Advertisement
Guest User

DOCXTemplate class 0.1.10

a guest
Mar 10th, 2015
330
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.82 KB | None | 0 0
  1. <?php
  2. /**
  3. * DOCXTemplate class 0.1.10 by sergey more at http://www.phpclasses.org/package/8247-PHP-Create-Microsoft-Word-documents-from-templates.html#applications
  4. * Replace {var} in MS Word 2007+ documents (*.docx)
  5.  
  6. [test.tpl.docx]
  7.  
  8. INVOICE {NUM} Invoice date {COMPANY}
  9. {DATE}
  10.  
  11. <?php // test.php
  12.  
  13. include('docxtemplate.class.php');
  14.  
  15. $docx = new DOCXTemplate('test.tpl.docx');
  16. $docx->set('NUM', 123456 );
  17. $docx->set('DATE', date('m.d.Y'));
  18. $docx->set('COMPANY', 'SIBVISION.RU
  19. Russian Federation, Omsk
  20. phone: +73812590554');
  21.  
  22. $docx->saveAs('test.docx'); // or $docx->downloadAs('test.docx');
  23.  
  24. */
  25.  
  26. class DOCXTemplate {
  27. private $data;
  28. private $package;
  29. private $error;
  30. public function __construct( $template_filename, $is_data = false, $debug = false ) {
  31. $this->data = array();
  32. $this->error = false;
  33. $this->debug = $debug;
  34. $this->_unzip( $template_filename, $is_data );
  35. }
  36. public function set( $var, $value = NULL ) {
  37.  
  38. if (is_array($var) || is_object($var)) {
  39. foreach( $var as $k => $v )
  40. $this->data[ $k ] = $v;
  41. } else {
  42. $this->data[ $var ] = $value;
  43. }
  44.  
  45. }
  46. public function error( $set = false ) {
  47. if ($set) {
  48. $this->error = $set;
  49. if ($this->debug)
  50. trigger_error( __CLASS__.': '.$set, E_USER_WARNING );
  51. } else {
  52. return $this->error;
  53. }
  54. }
  55. public function success() {
  56. return !$this->error;
  57. }
  58.  
  59. private function _parse() {
  60. if ($doc = $this->getEntryData( 'word/document.xml' )) {
  61. if (preg_match_all('/\{[^}]+\}/', $doc, $m )) {
  62. foreach( $m[0] as $v ) {
  63. $var = preg_replace('/[\s\{\}]/','', strip_tags( $v ));
  64. if (isset( $this->data[ $var ] )) {
  65. if (is_scalar( $this->data[ $var ] ))
  66. $doc = str_replace( $v, $this->_esc( $this->data[ $var ] ), $doc );
  67. } else {
  68. $doc = str_replace( $v, '{!404_'.__CLASS__.'_'.$var.'}', $doc );
  69. }
  70. }
  71. }
  72. $this->setEntryData( 'word/document.xml', $doc );
  73. return true;
  74. } else
  75. return false;
  76. }
  77.  
  78. private function _unzip( $filename, $is_data = false ) {
  79.  
  80. // Clear current file
  81. $this->datasec = array();
  82.  
  83. if ($is_data) {
  84.  
  85. $this->package['filename'] = 'default.xlsx';
  86. $this->package['mtime'] = time();
  87. $this->package['size'] = strlen( $filename );
  88.  
  89. $vZ = $filename;
  90. } else {
  91.  
  92. if (!is_readable($filename)) {
  93. $this->error( 'File not found' );
  94. return false;
  95. }
  96.  
  97. // Package information
  98. $this->package['filename'] = $filename;
  99. $this->package['mtime'] = filemtime( $filename );
  100. $this->package['size'] = filesize( $filename );
  101.  
  102. // Read file
  103. $oF = fopen($filename, 'rb');
  104. $vZ = fread($oF, $this->package['size']);
  105. fclose($oF);
  106.  
  107. }
  108. // Cut end of central directory
  109. /* $aE = explode("\x50\x4b\x05\x06", $vZ);
  110.  
  111. if (count($aE) == 1) {
  112. $this->error('Unknown format');
  113. return false;
  114. }
  115. */
  116. if ( ($pcd = strrpos( $vZ, "\x50\x4b\x05\x06" )) === false ) {
  117. $this->error('Unknown format');
  118. return false;
  119. }
  120. $aE = array(
  121. 0 => substr( $vZ, 0, $pcd ),
  122. 1 => substr( $vZ, $pcd + 3 )
  123. );
  124.  
  125. // Normal way
  126. $aP = unpack('x16/v1CL', $aE[1]);
  127. $this->package['comment'] = substr($aE[1], 18, $aP['CL']);
  128.  
  129. // Translates end of line from other operating systems
  130. $this->package['comment'] = strtr($this->package['comment'], array("\r\n" => "\n", "\r" => "\n"));
  131.  
  132. // Cut the entries from the central directory
  133. $aE = explode("\x50\x4b\x01\x02", $vZ);
  134. // Explode to each part
  135. $aE = explode("\x50\x4b\x03\x04", $aE[0]);
  136. // Shift out spanning signature or empty entry
  137. array_shift($aE);
  138.  
  139. // Loop through the entries
  140. foreach ($aE as $vZ) {
  141. $aI = array();
  142. $aI['E'] = 0;
  143. $aI['EM'] = '';
  144. // Retrieving local file header information
  145. // $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ);
  146. $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL/v1EFL', $vZ);
  147.  
  148. // Check if data is encrypted
  149. // $bE = ($aP['GPF'] && 0x0001) ? TRUE : FALSE;
  150. $bE = false;
  151. $nF = $aP['FNL'];
  152. $mF = $aP['EFL'];
  153.  
  154. // Special case : value block after the compressed data
  155. if ($aP['GPF'] & 0x0008) {
  156. $aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12));
  157.  
  158. $aP['CRC'] = $aP1['CRC'];
  159. $aP['CS'] = $aP1['CS'];
  160. $aP['UCS'] = $aP1['UCS'];
  161. // 2013-08-10
  162. $vZ = substr($vZ, 0, -12);
  163. if (substr($vZ,-4) === "\x50\x4b\x07\x08")
  164. $vZ = substr($vZ, 0, -4);
  165. }
  166.  
  167. // Getting stored filename
  168. $aI['N'] = substr($vZ, 26, $nF);
  169.  
  170. if (substr($aI['N'], -1) == '/') {
  171. // is a directory entry - will be skipped
  172. continue;
  173. }
  174.  
  175. // Truncate full filename in path and filename
  176. $aI['P'] = dirname($aI['N']);
  177. $aI['P'] = $aI['P'] == '.' ? '' : $aI['P'];
  178. $aI['N'] = basename($aI['N']);
  179.  
  180. $vZ = substr($vZ, 26 + $nF + $mF);
  181.  
  182. if ( strlen($vZ) != $aP['CS'] ) { // check only if availabled
  183. $aI['E'] = 1;
  184. $aI['EM'] = 'Compressed size is not equal with the value in header information.';
  185. } else {
  186. if ($bE) {
  187. $aI['E'] = 5;
  188. $aI['EM'] = 'File is encrypted, which is not supported from this class.';
  189. } else {
  190. switch($aP['CM']) {
  191. case 0: // Stored
  192. // Here is nothing to do, the file ist flat.
  193. break;
  194. case 8: // Deflated
  195. $vZ = gzinflate($vZ);
  196. break;
  197. case 12: // BZIP2
  198. if (! extension_loaded('bz2')) {
  199. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
  200. @dl('php_bz2.dll');
  201. } else {
  202. @dl('bz2.so');
  203. }
  204. }
  205. if (extension_loaded('bz2')) {
  206. $vZ = bzdecompress($vZ);
  207. } else {
  208. $aI['E'] = 7;
  209. $aI['EM'] = "PHP BZIP2 extension not available.";
  210. }
  211. break;
  212. default:
  213. $aI['E'] = 6;
  214. $aI['EM'] = "De-/Compression method {$aP['CM']} is not supported.";
  215. }
  216. if (! $aI['E']) {
  217. if ($vZ === FALSE) {
  218. $aI['E'] = 2;
  219. $aI['EM'] = 'Decompression of data failed.';
  220. } else {
  221. if (strlen($vZ) != $aP['UCS']) {
  222. $aI['E'] = 3;
  223. $aI['EM'] = 'Uncompressed size is not equal with the value in header information.';
  224. } else {
  225. if (crc32($vZ) != $aP['CRC']) {
  226. $aI['E'] = 4;
  227. $aI['EM'] = 'CRC32 checksum is not equal with the value in header information.';
  228. }
  229. }
  230. }
  231. }
  232. }
  233. }
  234.  
  235. $aI['D'] = $vZ;
  236.  
  237. // DOS to UNIX timestamp
  238. $aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11,
  239. ($aP['FT'] & 0x07e0) >> 5,
  240. ($aP['FT'] & 0x001f) << 1,
  241. ($aP['FD'] & 0x01e0) >> 5,
  242. ($aP['FD'] & 0x001f),
  243. (($aP['FD'] & 0xfe00) >> 9) + 1980);
  244.  
  245. //$this->Entries[] = &new SimpleUnzipEntry($aI);
  246. $this->package['entries'][] = array(
  247. 'data' => $aI['D'],
  248. 'error' => $aI['E'],
  249. 'error_msg' => $aI['EM'],
  250. 'name' => $aI['N'],
  251. 'path' => $aI['P'],
  252. 'time' => $aI['T']
  253. );
  254.  
  255. } // end for each entries
  256. }
  257. public function getPackage() {
  258. return $this->package;
  259. }
  260. public function entryExists( $name ) { // 0.6.6
  261. $dir = dirname( $name );
  262. $name = basename( $name );
  263. foreach( $this->package['entries'] as $entry)
  264. if ( $entry['path'] == $dir && $entry['name'] == $name)
  265. return true;
  266. return false;
  267. }
  268. public function getEntryData( $name ) {
  269. $dir = dirname( $name );
  270. $name = basename( $name );
  271. foreach( $this->package['entries'] as $entry)
  272. if ( $entry['path'] == $dir && $entry['name'] == $name)
  273. return $entry['data'];
  274. $this->error('Unknown format');
  275. return false;
  276. }
  277. public function setEntryData( $name, $data ) {
  278. $dir = dirname( $name );
  279. $name = basename( $name );
  280. foreach( $this->package['entries'] as $k => $entry)
  281. if ( $entry['path'] == $dir && $entry['name'] == $name) {
  282. $this->package['entries'][$k]['data'] = $data;
  283. return;
  284. }
  285. return false;
  286. }
  287.  
  288. private function _esc( $s ) {
  289. $s = str_replace(array('&','"',"'","<",">"),array('&amp;','&quot;','&#039;','&lt;','&gt;'), $s);
  290. $s = str_replace( array("\r\n", "\r"),"\n", $s );
  291. $s = str_replace( "\n", '</w:t><w:br/><w:t>', $s );
  292. return $s;
  293. }
  294.  
  295. private function _zip( $fh ) {
  296.  
  297. $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
  298. $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
  299. $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
  300.  
  301. $zipComments = 'Generated by '.__CLASS__.' PHP class, thanks Sergey Shuchkin';
  302.  
  303. // $fh = fopen( $filename, 'wb' );
  304.  
  305. if (!$fh)
  306. return false;
  307.  
  308. $cdrec = '';
  309.  
  310. foreach ($this->package['entries'] as $e ) {
  311.  
  312. $cfilename = ($e['path']) ? $e['path'] .'/'. $e['name'] : $e['name'];
  313.  
  314.  
  315. $e['uncsize'] = strlen($e['data']);
  316.  
  317. // if data to compress is too small, just store it
  318. if($e['uncsize'] < 256){
  319. $e['comsize'] = $e['uncsize'];
  320. $e['vneeded'] = 10;
  321. $e['cmethod'] = 0;
  322. $zdata = $e['data'];
  323. } else{ // otherwise, compress it
  324. $zdata = gzcompress($e['data']);
  325. $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug (thanks to Eric Mueller)
  326. $e['comsize'] = strlen($zdata);
  327. $e['vneeded'] = 10;
  328. $e['cmethod'] = 8;
  329. }
  330.  
  331. $e['bitflag'] = 0;
  332. $e['crc_32'] = crc32($e['data']);
  333.  
  334. // Convert date and time to DOS Format, and set then
  335. $lastmod_timeS = str_pad(decbin(date('s')>=32?date('s')-32:date('s')), 5, '0', STR_PAD_LEFT);
  336. $lastmod_timeM = str_pad(decbin(date('i')), 6, '0', STR_PAD_LEFT);
  337. $lastmod_timeH = str_pad(decbin(date('H')), 5, '0', STR_PAD_LEFT);
  338. $lastmod_dateD = str_pad(decbin(date('d')), 5, '0', STR_PAD_LEFT);
  339. $lastmod_dateM = str_pad(decbin(date('m')), 4, '0', STR_PAD_LEFT);
  340. $lastmod_dateY = str_pad(decbin(date('Y')-1980), 7, '0', STR_PAD_LEFT);
  341.  
  342. # echo "ModTime: $lastmod_timeS-$lastmod_timeM-$lastmod_timeH (".date("s H H").")\n";
  343. # echo "ModDate: $lastmod_dateD-$lastmod_dateM-$lastmod_dateY (".date("d m Y").")\n";
  344. $e['modtime'] = bindec("$lastmod_timeH$lastmod_timeM$lastmod_timeS");
  345. $e['moddate'] = bindec("$lastmod_dateY$lastmod_dateM$lastmod_dateD");
  346.  
  347. $e['offset'] = ftell($fh);
  348.  
  349. fwrite($fh, $zipSignature);
  350. fwrite($fh, pack('s', $e['vneeded'])); // version_needed
  351. fwrite($fh, pack('s', $e['bitflag'])); // general_bit_flag
  352. fwrite($fh, pack('s', $e['cmethod'])); // compression_method
  353. fwrite($fh, pack('s', $e['modtime'])); // lastmod_time
  354. fwrite($fh, pack('s', $e['moddate'])); // lastmod_date
  355. fwrite($fh, pack('V', $e['crc_32'])); // crc-32
  356. fwrite($fh, pack('I', $e['comsize'])); // compressed_size
  357. fwrite($fh, pack('I', $e['uncsize'])); // uncompressed_size
  358. fwrite($fh, pack('s', strlen($cfilename))); // file_name_length
  359. fwrite($fh, pack('s', 0)); // extra_field_length
  360. fwrite($fh, $cfilename); // file_name
  361. // ignoring extra_field
  362. fwrite($fh, $zdata);
  363.  
  364. // Append it to central dir
  365. $e['external_attributes'] = (substr($cfilename, -1)=='/'&&!$zdata)?16:32; // Directory or file name
  366. $e['comments'] = '';
  367.  
  368. $cdrec .= $dirSignature;
  369. $cdrec .= "\x0\x0"; // version made by
  370. $cdrec .= pack('v', $e['vneeded']); // version needed to extract
  371. $cdrec .= "\x0\x0"; // general bit flag
  372. $cdrec .= pack('v', $e['cmethod']); // compression method
  373. $cdrec .= pack('v', $e['modtime']); // lastmod time
  374. $cdrec .= pack('v', $e['moddate']); // lastmod date
  375. $cdrec .= pack('V', $e['crc_32']); // crc32
  376. $cdrec .= pack('V', $e['comsize']); // compressed filesize
  377. $cdrec .= pack('V', $e['uncsize']); // uncompressed filesize
  378. $cdrec .= pack('v', strlen($cfilename)); // file name length
  379. $cdrec .= pack('v', 0); // extra field length
  380. $cdrec .= pack('v', strlen($e['comments'])); // file comment length
  381. $cdrec .= pack('v', 0); // disk number start
  382. $cdrec .= pack('v', 0); // internal file attributes
  383. $cdrec .= pack('V', $e['external_attributes']); // internal file attributes
  384. $cdrec .= pack('V', $e['offset']); // relative offset of local header
  385. $cdrec .= $cfilename;
  386. $cdrec .= $e['comments'];
  387. }
  388. $before_cd = ftell($fh);
  389. fwrite($fh, $cdrec);
  390.  
  391. // end of central dir
  392. fwrite($fh, $dirSignatureE);
  393. fwrite($fh, pack('v', 0)); // number of this disk
  394. fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory
  395. fwrite($fh, pack('v', count( $this->package['entries'] ))); // total # of entries "on this disk"
  396. fwrite($fh, pack('v', count( $this->package['entries'] ))); // total # of entries overall
  397. fwrite($fh, pack('V', strlen($cdrec))); // size of central dir
  398. fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
  399. fwrite($fh, pack('v', strlen($zipComments))); // .zip file comment length
  400. fwrite($fh, $zipComments);
  401.  
  402. return true;
  403. }
  404. function saveAs( $filename ) {
  405.  
  406. if (!$this->_parse())
  407. return false;
  408.  
  409. $fh = fopen( $filename, 'wb' );
  410. if (!$fh)
  411. return false;
  412.  
  413. if (!$this->_zip($fh)) {
  414. fclose($fh);
  415. return false;
  416. }
  417. fclose($fh);
  418.  
  419. chmod($filename, 0600);
  420.  
  421. return true;
  422. }
  423. function downloadAs( $filename, $exit = true ) {
  424.  
  425. if (!$this->_parse())
  426. return false;
  427. //php://stdin
  428.  
  429. $fh = tmpfile();
  430. if (!$fh)
  431. return false;
  432.  
  433. if (!$this->_zip( $fh )) {
  434. fclose( $fh );
  435. return false;
  436. }
  437.  
  438. $size = ftell($fh);
  439.  
  440. $filename = ($filename) ? $filename : gmdate('Ymdhi');
  441.  
  442. header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  443. header('Content-Disposition: attachment; filename="'.$filename.'"');
  444. header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T' , time() ));
  445. header('Content-Length: '.$size);
  446.  
  447. fseek($fh,0);
  448. echo fread($fh, $size );
  449.  
  450. fclose($fh);
  451.  
  452. if ($exit) exit();
  453. return true;
  454. }
  455. }
  456.  
  457.  
  458. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement