/** * @param Parameters|array|bool $file * @param int $itemId * @param \DDD\Domain\Finance\Expense\Expenses $expenseData * @return array * @throws \Exception */ private function saveItemFilePhisically(Parameters $file, $itemId, $expenseData) { $fileReport = []; if ($file->count()) { $file = $file->get('file'); $expenseItemDao = new ExpenseItem($this->getServiceLocator(), '\\ArrayObject'); $expenseId = $expenseItemDao->fetchOne(['id' => $itemId], ['expense_id'])['expense_id']; $expenseItemAttachmentsDao = $this->getServiceLocator()->get('dao_finance_expense_expense_item_attachments'); $isTempItem = empty($expenseData) ? true : false; if ($isTempItem) { $path = "/ginosi/uploads/expense/items_tmp/{$itemId}/"; } else { list($date, ) = explode(' ', $expenseData->getDateCreated()); list($y, $m, $d) = explode('-', $date); $path = "/ginosi/uploads/expense/items/{$y}/{$m}/{$d}/{$expenseData->getId()}/{$itemId}/"; } if (!is_readable($path)) { if (!mkdir($path, 0755, true)) { throw new \Exception('Cannot create directories.'); } } $fileName = $this->generateItemFileName($file['name'], $itemId); $oldInfo = $expenseItemAttachmentsDao->fetchOne(['item_id' => $itemId], ['id']); if ($this->isValidFile($file)) { if (!$isTempItem) { array_map('unlink', glob($path . '*')); } if (move_uploaded_file($file['tmp_name'], $path . $fileName)) { if ($oldInfo) { $expenseItemAttachmentsDao->save(['filename' => $fileName, 'expense_id' => $expenseId], ['item_id' => $itemId]); } else { $expenseItemAttachmentsDao->save(['item_id' => $itemId, 'filename' => $fileName, 'expense_id' => $expenseId]); } array_push($fileReport, ['value' => $fileName, 'status' => 'success']); } else { array_push($fileReport, ['value' => 'Cannot upload a file ' . $file['name'], 'status' => 'fail']); } } else { array_push($fileReport, ['value' => 'Unsupported file ' . $file['name'], 'status' => 'fail']); } } return $fileReport; }
/** * Parse upload content from a content stream * * @param resource $stream * @return array * @throws Exception\InvalidMultipartContentException */ protected function parseFromStream($stream) { $data = new Parameters(); $files = new Parameters(); $partInProgress = false; $inHeader = false; $headers = []; $header = false; $name = false; $content = ''; $file = []; $filename = false; $mimeType = false; $tmpFile = false; $partBoundaryPatternStart = '/^--' . $this->boundary . '(--)?/'; $partBoundaryPatternEnd = '/^--' . $this->boundary . '--$/'; while (false !== ($line = fgets($stream))) { $trimmedLine = rtrim($line); if (preg_match($partBoundaryPatternStart, $trimmedLine)) { if ($partInProgress) { // Time to handle the data we've already parsed! // Data if (!$filename) { $data->set($name, rtrim($content, "\r\n")); } // File (successful upload so far) if ($filename && $tmpFile) { // Write the last line, stripping the EOL characters if (false === fwrite($tmpFile, rtrim($lastline, "\r\n"))) { // Ooops! error writing the very last line! $file['error'] = UPLOAD_ERR_CANT_WRITE; fclose($tmpFile); } else { // Success! Let's try and guess the MIME type based on the file written fclose($tmpFile); if ($mimeType === 'application/octet-stream' && function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $type = finfo_file($finfo, $file['tmp_name']); if (false !== $type) { $file['type'] = $type; } } // Finally, set the filesize $file['size'] = filesize($file['tmp_name']); } } if ($filename) { // At this point, we can add the file entry, regardless of error condition $files->set($name, $file); } } // Is this a boundary end? If so, we're done if (preg_match($partBoundaryPatternEnd, $trimmedLine)) { // Met the "end" boundary; time to stop! break; } // New part to parse! $partInProgress = true; $inHeader = true; $headers = []; $header = ''; continue; } if (!$partInProgress) { // We're not inside a part, so do nothing. continue; } if ($inHeader) { if (preg_match('/^\\s*$/s', $line)) { // Headers are done; cleanup $inHeader = false; $content = ''; $file = ['error' => UPLOAD_ERR_OK]; $tmpFile = false; $lastline = null; // Parse headers $name = $this->getNameFromHeaders($headers); if (!$name) { throw new Exception\InvalidMultipartContentException('Missing Content-Disposition header, or Content-Disposition header does not ' . 'contain a "name" field'); } $filename = $this->getFilenameFromHeaders($headers); $mimeType = $this->getMimeTypeFromHeaders($headers); continue; } if (preg_match('/^(?P<header>[a-z]+[a-z0-9_-]+):\\s*(?P<value>.*)$/i', $trimmedLine, $matches)) { $header = strtoupper($matches['header']); $headers[$header] = $matches['value']; continue; } if (!$header) { throw new Exception\InvalidMultipartContentException('Malformed or missing MIME part header for multipart content'); } $headers[$header] .= $trimmedLine; continue; } // In the body content... // Data only; aggregate. if (!$filename) { $content .= $line; continue; } // If we've had an error already with the upload, continue parsing // to the end of the MIME part if ($file['error'] !== UPLOAD_ERR_OK) { continue; } // Create a temporary file handle if we haven't already if (!$tmpFile) { // Sets the file entry $file['name'] = $filename; $file['type'] = $mimeType; $tmpDir = $this->getUploadTempDir(); if (empty($tmpDir)) { // Cannot ascertain temporary directory; this is an error $file['error'] = UPLOAD_ERR_NO_TMP_DIR; continue; } $file['tmp_name'] = tempnam($tmpDir, 'zfc'); $tmpFile = fopen($file['tmp_name'], 'wb'); if (false === $tmpFile) { // Cannot open the temporary file for writing; this is an error $file['error'] = UPLOAD_ERR_CANT_WRITE; continue; } } // Off-by-one operation. Last line must be trimmed, so we'll write // the lines one iteration behind. if (null === $lastline) { $lastline = $line; continue; } if (false === fwrite($tmpFile, $lastline)) { $file['error'] = UPLOAD_ERR_CANT_WRITE; fclose($tmpFile); continue; } $lastline = $line; } fclose($stream); if ($files->count()) { $this->request->setFiles($files); } return $data->toArray(); }