select; } public function setSelect(Zend_Db_Select $select) { $alias = key($select->getPart(Zend_Db_Select::FROM)); $tableName = $select->getPart(Zend_Db_Select::FROM)[$alias]['tableName']; $this->table = new Qs_Db_Table(['name' => $tableName]); $select->reset(Zend_Db_Select::COLUMNS)->columns([ 'id', 'expired' => new Zend_Db_Expr( "IFNULL(`{$alias}`.`pdfCreatedAt`, '1980-01-01 00:00:00') < `{$alias}`.`changed`" ) ]); $select->reset(Zend_Db_Select::LIMIT_COUNT); $select->reset(Zend_Db_Select::LIMIT_OFFSET); $this->select = $select; return $this; } public function getTargetDir() { return $this->targetDir; } public function setTargetDir($targetDir) { if (empty($targetDir)) { throw new Exception('Empty target dir'); } if (!file_exists($targetDir) && false === mkdir($targetDir, 0755, true)) { throw new Exception('Can not create target dir: ' . $targetDir); } $this->targetDir = $targetDir; return $this; } public function getExportFile() { return $this->getTargetDir() . '/' . $this->getExportFileName(); } private function getExportFileName() { $select = ''. $this->getSelect(); return md5($select) . '.' . $this->getResultType(); } public function getResultType() { return $this->resultType; } public function setResultType($resultType) { $this->resultType = $resultType; return $this; } private function getPidFile() { return $this->getTargetDir() . '/process.pid'; } private function getStoredPid() { $pid = 0; $file = $this->getPidFile(); if (file_exists($file)) { $pid = file_get_contents($file); } return $pid; } private function savePid($pid) { $file = $this->getPidFile(); if (false === file_put_contents($file, $pid)) { throw new Exception('Failed write to file: ' . $file); } return $this; } public function isInProcess() { $pid = $this->getStoredPid(); return $pid && posix_getsid($pid); } protected function start() { if ($this->isInProcess()) { return false; } $myPid = getmypid(); $this->savePid($myPid); return true; } protected function clearStoredPid() { $this->savePid(0); return $this; } public function getItemFileName() { return $this->itemFileName; } public function setItemFileName($itemFileName) { $this->itemFileName = $itemFileName; return $this; } public function getPdfFactoryCallback() { return $this->pdfFactoryCallback; } public function setPdfFactoryCallback($pdfFactoryCallback) { $this->pdfFactoryCallback = $pdfFactoryCallback; return $this; } /** * @return void */ protected function runForever() { ini_set('max_execution_time', '0'); ini_set('ignore_user_abort', '1'); } public function export() { $this->runForever(); $this->start() || exit; $this->createPdfFiles(); $this->createZipArchive(); $this->clearStoredPid(); } private function createPdfFiles() { foreach ($this->getSelect()->query() as $item) { $file = $this->getTargetDir() . '/' . $this->getPdfItemFileName($item['id']); if (!file_exists($file) || $item['expired']) { call_user_func($this->getPdfFactoryCallback(), $item['id'], $file); $this->table->updateDataByKey(['pdfCreatedAt' => date('Y-m-d H:i:s')], $item['id']); } } return $this; } private function getPdfItemFileName($id) { return Qs_String::fill($this->getItemFileName(), compact('id')); } private function createZipArchive() { $currentDir = getcwd(); $dir = $this->getTargetDir(); chdir($dir); $zip = $this->getExportFileName(); $cmd = 'zip ' . escapeshellarg($zip); $item = null; foreach ($this->getSelect()->query() as $item) { $cmd .= ' ' . escapeshellarg($this->getPdfItemFileName($item['id'])); } if (null === $item) { chdir($currentDir); return false; } $output = []; $return = 0; exec($cmd, $output, $return); if (0 !== $return) { throw new Exception("Failed to create zip file. \nCommand {$cmd}\nOutput: " . join(PHP_EOL, $output)); } chdir($currentDir); return $dir . '/' . $zip; } public function download($fileName, $displayName = null) { $file = $this->getTargetDir() . '/' . $fileName; if (!file_exists($file)) { throw new Exception('File not found. Try to export again.'); } if (false === ($handle = fopen($file, 'r'))) { throw new Exception('Failed to open file ' . $file); } header('Content-Type: application/' . $this->getResultType()); if ($displayName) { $disabledCharacters = ['<', '>', '\\', '"', '/', ':', '|', '?', '*']; $displayName = str_replace('{date}', date('m-d-y_g-i-A', filemtime($file)), $displayName); $displayName = str_replace($disabledCharacters, '', $displayName); header('Content-Disposition: attachment; filename="' . $displayName . '"'); } fpassthru($handle); fclose($handle); exit; } }