General['enable_load_data_infile']; if ($loadDataInfileEnabled && Db::get()->hasBulkLoader()) { try { $fileSpec = array( 'delim' => "\t", 'quote' => '"', // chr(34) 'escape' => '\\\\', // chr(92) 'escapespecial_cb' => function ($str) { return str_replace(array(chr(92), chr(34)), array(chr(92) . chr(92), chr(92) . chr(34)), $str); }, 'eol' => "\r\n", 'null' => 'NULL', ); // hack for charset mismatch if (!DbHelper::isDatabaseConnectionUTF8() && !isset(Config::getInstance()->database['charset'])) { $fileSpec['charset'] = 'latin1'; } self::createCSVFile($filePath, $fileSpec, $values); if (!is_readable($filePath)) { throw new Exception("File $filePath could not be read."); } $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec); if ($rc) { unlink($filePath); return true; } } catch (Exception $e) { Log::info("LOAD DATA INFILE failed or not supported, falling back to normal INSERTs... Error was: %s", $e->getMessage()); if ($throwException) { throw $e; } } } // if all else fails, fallback to a series of INSERTs @unlink($filePath); self::tableInsertBatchIterate($tableName, $fields, $values); return false; } /** * Batch insert into table from CSV (or other delimited) file. * * @param string $tableName Name of table * @param array $fields Field names * @param string $filePath Path name of a file. * @param array $fileSpec File specifications (delimiter, line terminator, etc) * * @throws Exception * @return bool True if successful; false otherwise */ public static function createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec) { // Chroot environment: prefix the path with the absolute chroot path $chrootPath = Config::getInstance()->General['absolute_chroot_path']; if (!empty($chrootPath)) { $filePath = $chrootPath . $filePath; } // On Windows, MySQL expects forward slashes as directory separators if (SettingsServer::isWindows()) { $filePath = str_replace('\\', '/', $filePath); } $query = " '$filePath' REPLACE INTO TABLE `" . $tableName . "`"; if (isset($fileSpec['charset'])) { $query .= ' CHARACTER SET ' . $fileSpec['charset']; } $fieldList = '(' . join(',', $fields) . ')'; $query .= " FIELDS TERMINATED BY '" . $fileSpec['delim'] . "' ENCLOSED BY '" . $fileSpec['quote'] . "' "; if (isset($fileSpec['escape'])) { $query .= " ESCAPED BY '" . $fileSpec['escape'] . "'"; } $query .= " LINES TERMINATED BY '" . $fileSpec['eol'] . "' $fieldList "; /* * First attempt: assume web server and MySQL server are on the same machine; * this requires that the db user have the FILE privilege; however, since this is * a global privilege, it may not be granted due to security concerns */ $keywords = array(''); /* * Second attempt: using the LOCAL keyword means the client reads the file and sends it to the server; * the LOCAL keyword may trigger a known PHP PDO\MYSQL bug when MySQL not built with --enable-local-infile * @see http://bugs.php.net/bug.php?id=54158 */ $openBaseDir = ini_get('open_basedir'); $safeMode = ini_get('safe_mode'); if (empty($openBaseDir) && empty($safeMode)) { // php 5.x - LOAD DATA LOCAL INFILE is disabled if open_basedir restrictions or safe_mode enabled $keywords[] = 'LOCAL '; } $exceptions = array(); foreach ($keywords as $keyword) { $queryStart = 'LOAD DATA ' . $keyword . 'INFILE '; $sql = $queryStart . $query; try { $result = @Db::exec($sql); if (empty($result) || $result < 0) { continue; } return true; } catch (Exception $e) { // echo $sql . ' ---- ' . $e->getMessage(); $code = $e->getCode(); $message = $e->getMessage() . ($code ? "[$code]" : ''); if (!Db::get()->isErrNo($e, '1148')) { Log::info("LOAD DATA INFILE failed... Error was: %s", $message); } $exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message; } } if (count($exceptions)) { throw new Exception(implode(",", $exceptions)); } return false; } /** * Create CSV (or other delimited) files * * @param string $filePath filename to create * @param array $fileSpec File specifications (delimiter, line terminator, etc) * @param array $rows Array of array corresponding to rows of values * @throws Exception if unable to create or write to file */ protected static function createCSVFile($filePath, $fileSpec, $rows) { // Set up CSV delimiters, quotes, etc $delim = $fileSpec['delim']; $quote = $fileSpec['quote']; $eol = $fileSpec['eol']; $null = $fileSpec['null']; $escapespecial_cb = $fileSpec['escapespecial_cb']; $fp = @fopen($filePath, 'wb'); if (!$fp) { throw new Exception('Error creating the tmp file ' . $filePath . ', please check that the webserver has write permission to write this file.'); } foreach ($rows as $row) { $output = ''; foreach ($row as $value) { if (!isset($value) || is_null($value) || $value === false) { $output .= $null . $delim; } else { $output .= $quote . $escapespecial_cb($value) . $quote . $delim; } } // Replace delim with eol $output = substr_replace($output, $eol, -1); $ret = fwrite($fp, $output); if (!$ret) { fclose($fp); throw new Exception('Error writing to the tmp file ' . $filePath); } } fclose($fp); @chmod($filePath, 0777); } }