Initial commit

This commit is contained in:
2022-11-21 09:47:28 +01:00
commit 76cec83d26
11652 changed files with 1980467 additions and 0 deletions

View File

@ -0,0 +1,92 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Super class of CSV import plugins for phpMyAdmin
*
* @package PhpMyAdmin-Import
* @subpackage CSV
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\options\items\BoolPropertyItem;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA\libraries\properties\options\groups\OptionsPropertyMainGroup;
use PMA\libraries\properties\options\groups\OptionsPropertyRootGroup;
use PMA\libraries\plugins\ImportPlugin;
use PMA\libraries\properties\options\items\TextPropertyItem;
/**
* Super class of the import plugins for the CSV format
*
* @package PhpMyAdmin-Import
* @subpackage CSV
*/
abstract class AbstractImportCsv extends ImportPlugin
{
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return \PMA\libraries\properties\options\groups\OptionsPropertyMainGroup PMA\libraries\properties\options\groups\OptionsPropertyMainGroup object of the plugin
*/
protected function setProperties()
{
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setOptionsText(__('Options'));
// create the root group that will be the options field for
// $importPluginProperties
// this will be shown as "Format specific options"
$importSpecificOptions = new OptionsPropertyRootGroup(
"Format Specific Options"
);
// general options main group
$generalOptions = new OptionsPropertyMainGroup("general_opts");
// create common items and add them to the group
$leaf = new BoolPropertyItem(
"replace",
__(
'Update data when duplicate keys found on import (add ON DUPLICATE '
. 'KEY UPDATE)'
)
);
$generalOptions->addProperty($leaf);
$leaf = new TextPropertyItem(
"terminated",
__('Columns separated with:')
);
$leaf->setSize(2);
$generalOptions->addProperty($leaf);
$leaf = new TextPropertyItem(
"enclosed",
__('Columns enclosed with:')
);
$leaf->setSize(2);
$leaf->setLen(2);
$generalOptions->addProperty($leaf);
$leaf = new TextPropertyItem(
"escaped",
__('Columns escaped with:')
);
$leaf->setSize(2);
$leaf->setLen(2);
$generalOptions->addProperty($leaf);
$leaf = new TextPropertyItem(
"new_line",
__('Lines terminated with:')
);
$leaf->setSize(2);
$generalOptions->addProperty($leaf);
// add the main group to the root group
$importSpecificOptions->addProperty($generalOptions);
// set the options for the import plugin property item
$importPluginProperties->setOptions($importSpecificOptions);
$this->properties = $importPluginProperties;
return $generalOptions;
}
}

View File

@ -0,0 +1,716 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* CSV import plugin for phpMyAdmin
*
* @todo add an option for handling NULL values
* @package PhpMyAdmin-Import
* @subpackage CSV
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\options\items\BoolPropertyItem;
use PMA;
use PMA\libraries\properties\options\items\TextPropertyItem;
/**
* Handles the import for the CSV format
*
* @package PhpMyAdmin-Import
* @subpackage CSV
*/
class ImportCsv extends AbstractImportCsv
{
/**
* Whether to analyze tables
*
* @var bool
*/
private $_analyze;
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$this->_setAnalyze(false);
if ($GLOBALS['plugin_param'] !== 'table') {
$this->_setAnalyze(true);
}
$generalOptions = parent::setProperties();
$this->properties->setText('CSV');
$this->properties->setExtension('csv');
if ($GLOBALS['plugin_param'] !== 'table') {
$leaf = new BoolPropertyItem(
"col_names",
__(
'The first line of the file contains the table column names'
. ' <i>(if this is unchecked, the first line will become part'
. ' of the data)</i>'
)
);
$generalOptions->addProperty($leaf);
} else {
$hint = new PMA\libraries\Message(
__(
'If the data in each row of the file is not'
. ' in the same order as in the database, list the corresponding'
. ' column names here. Column names must be separated by commas'
. ' and not enclosed in quotations.'
)
);
$leaf = new TextPropertyItem(
"columns",
__('Column names: ') . PMA\libraries\Util::showHint($hint)
);
$generalOptions->addProperty($leaf);
}
$leaf = new BoolPropertyItem(
"ignore",
__('Do not abort on INSERT error')
);
$generalOptions->addProperty($leaf);
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $db, $table, $csv_terminated, $csv_enclosed, $csv_escaped,
$csv_new_line, $csv_columns, $err_url;
// $csv_replace and $csv_ignore should have been here,
// but we use directly from $_POST
global $error, $timeout_passed, $finished, $message;
$replacements = array(
'\\n' => "\n",
'\\t' => "\t",
'\\r' => "\r",
);
$csv_terminated = strtr($csv_terminated, $replacements);
$csv_enclosed = strtr($csv_enclosed, $replacements);
$csv_escaped = strtr($csv_escaped, $replacements);
$csv_new_line = strtr($csv_new_line, $replacements);
$param_error = false;
if (mb_strlen($csv_terminated) < 1) {
$message = PMA\libraries\Message::error(
__('Invalid parameter for CSV import: %s')
);
$message->addParam(__('Columns terminated with'), false);
$error = true;
$param_error = true;
// The default dialog of MS Excel when generating a CSV produces a
// semi-colon-separated file with no chance of specifying the
// enclosing character. Thus, users who want to import this file
// tend to remove the enclosing character on the Import dialog.
// I could not find a test case where having no enclosing characters
// confuses this script.
// But the parser won't work correctly with strings so we allow just
// one character.
} elseif (mb_strlen($csv_enclosed) > 1) {
$message = PMA\libraries\Message::error(
__('Invalid parameter for CSV import: %s')
);
$message->addParam(__('Columns enclosed with'), false);
$error = true;
$param_error = true;
// I could not find a test case where having no escaping characters
// confuses this script.
// But the parser won't work correctly with strings so we allow just
// one character.
} elseif (mb_strlen($csv_escaped) > 1) {
$message = PMA\libraries\Message::error(
__('Invalid parameter for CSV import: %s')
);
$message->addParam(__('Columns escaped with'), false);
$error = true;
$param_error = true;
} elseif (mb_strlen($csv_new_line) != 1
&& $csv_new_line != 'auto'
) {
$message = PMA\libraries\Message::error(
__('Invalid parameter for CSV import: %s')
);
$message->addParam(__('Lines terminated with'), false);
$error = true;
$param_error = true;
}
// If there is an error in the parameters entered,
// indicate that immediately.
if ($param_error) {
PMA\libraries\Util::mysqlDie(
$message->getMessage(),
'',
false,
$err_url
);
}
$buffer = '';
$required_fields = 0;
if (!$this->_getAnalyze()) {
$sql_template = 'INSERT';
if (isset($_POST['csv_ignore'])) {
$sql_template .= ' IGNORE';
}
$sql_template .= ' INTO ' . PMA\libraries\Util::backquote($table);
$tmp_fields = $GLOBALS['dbi']->getColumns($db, $table);
if (empty($csv_columns)) {
$fields = $tmp_fields;
} else {
$sql_template .= ' (';
$fields = array();
$tmp = preg_split('/,( ?)/', $csv_columns);
foreach ($tmp as $key => $val) {
if (count($fields) > 0) {
$sql_template .= ', ';
}
/* Trim also `, if user already included backquoted fields */
$val = trim($val, " \t\r\n\0\x0B`");
$found = false;
foreach ($tmp_fields as $field) {
if ($field['Field'] == $val) {
$found = true;
break;
}
}
if (!$found) {
$message = PMA\libraries\Message::error(
__(
'Invalid column (%s) specified! Ensure that columns'
. ' names are spelled correctly, separated by commas'
. ', and not enclosed in quotes.'
)
);
$message->addParam($val);
$error = true;
break;
}
$fields[] = $field;
$sql_template .= PMA\libraries\Util::backquote($val);
}
$sql_template .= ') ';
}
$required_fields = count($fields);
$sql_template .= ' VALUES (';
}
// Defaults for parser
$i = 0;
$len = 0;
$lastlen = null;
$line = 1;
$lasti = -1;
$values = array();
$csv_finish = false;
$tempRow = array();
$rows = array();
$col_names = array();
$tables = array();
$col_count = 0;
$max_cols = 0;
$csv_terminated_len = mb_strlen($csv_terminated);
while (!($finished && $i >= $len) && !$error && !$timeout_passed) {
$data = PMA_importGetNextChunk();
if ($data === false) {
// subtract data we didn't handle yet and stop processing
$GLOBALS['offset'] -= strlen($buffer);
break;
} elseif ($data === true) {
// Handle rest of buffer
} else {
// Append new data to buffer
$buffer .= $data;
unset($data);
// Force a trailing new line at EOF to prevent parsing problems
if ($finished && $buffer) {
$finalch = mb_substr($buffer, -1);
if ($csv_new_line == 'auto'
&& $finalch != "\r"
&& $finalch != "\n"
) {
$buffer .= "\n";
} elseif ($csv_new_line != 'auto'
&& $finalch != $csv_new_line
) {
$buffer .= $csv_new_line;
}
}
// Do not parse string when we're not at the end
// and don't have new line inside
if (($csv_new_line == 'auto'
&& mb_strpos($buffer, "\r") === false
&& mb_strpos($buffer, "\n") === false)
|| ($csv_new_line != 'auto'
&& mb_strpos($buffer, $csv_new_line) === false)
) {
continue;
}
}
// Current length of our buffer
$len = mb_strlen($buffer);
// Currently parsed char
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
while ($i < $len) {
// Deadlock protection
if ($lasti == $i && $lastlen == $len) {
$message = PMA\libraries\Message::error(
__('Invalid format of CSV input on line %d.')
);
$message->addParam($line);
$error = true;
break;
}
$lasti = $i;
$lastlen = $len;
// This can happen with auto EOL and \r at the end of buffer
if (!$csv_finish) {
// Grab empty field
if ($ch == $csv_terminated) {
if ($i == $len - 1) {
break;
}
$values[] = '';
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
continue;
}
// Grab one field
$fallbacki = $i;
if ($ch == $csv_enclosed) {
if ($i == $len - 1) {
break;
}
$need_end = true;
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
} else {
$need_end = false;
}
$fail = false;
$value = '';
while (($need_end
&& ($ch != $csv_enclosed
|| $csv_enclosed == $csv_escaped))
|| (!$need_end
&& !($ch == $csv_terminated
|| $ch == $csv_new_line
|| ($csv_new_line == 'auto'
&& ($ch == "\r" || $ch == "\n"))))
) {
if ($ch == $csv_escaped) {
if ($i == $len - 1) {
$fail = true;
break;
}
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1
&& $ch == $csv_terminated[0]
) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
if ($csv_enclosed == $csv_escaped
&& ($ch == $csv_terminated
|| $ch == $csv_new_line
|| ($csv_new_line == 'auto'
&& ($ch == "\r" || $ch == "\n")))
) {
break;
}
}
$value .= $ch;
if ($i == $len - 1) {
if (!$finished) {
$fail = true;
}
break;
}
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
}
// unquoted NULL string
if (false === $need_end && $value === 'NULL') {
$value = null;
}
if ($fail) {
$i = $fallbacki;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1 && $ch == $csv_terminated[0]) {
$i += $csv_terminated_len - 1;
}
break;
}
// Need to strip trailing enclosing char?
if ($need_end && $ch == $csv_enclosed) {
if ($finished && $i == $len - 1) {
$ch = null;
} elseif ($i == $len - 1) {
$i = $fallbacki;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1
&& $ch == $csv_terminated[0]
) {
$i += $csv_terminated_len - 1;
}
break;
} else {
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1
&& $ch == $csv_terminated[0]
) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
}
}
// Are we at the end?
if ($ch == $csv_new_line
|| ($csv_new_line == 'auto' && ($ch == "\r" || $ch == "\n"))
|| ($finished && $i == $len - 1)
) {
$csv_finish = true;
}
// Go to next char
if ($ch == $csv_terminated) {
if ($i == $len - 1) {
$i = $fallbacki;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1
&& $ch == $csv_terminated[0]
) {
$i += $csv_terminated_len - 1;
}
break;
}
$i++;
$ch = mb_substr($buffer, $i, 1);
if ($csv_terminated_len > 1
&& $ch == $csv_terminated[0]
) {
$ch = $this->readCsvTerminatedString(
$buffer,
$ch,
$i,
$csv_terminated_len
);
$i += $csv_terminated_len - 1;
}
}
// If everything went okay, store value
$values[] = $value;
}
// End of line
if ($csv_finish
|| $ch == $csv_new_line
|| ($csv_new_line == 'auto' && ($ch == "\r" || $ch == "\n"))
) {
if ($csv_new_line == 'auto' && $ch == "\r") { // Handle "\r\n"
if ($i >= ($len - 2) && !$finished) {
break; // We need more data to decide new line
}
if (mb_substr($buffer, $i + 1, 1) == "\n") {
$i++;
}
}
// We didn't parse value till the end of line, so there was
// empty one
if (!$csv_finish) {
$values[] = '';
}
if ($this->_getAnalyze()) {
foreach ($values as $val) {
$tempRow[] = $val;
++$col_count;
}
if ($col_count > $max_cols) {
$max_cols = $col_count;
}
$col_count = 0;
$rows[] = $tempRow;
$tempRow = array();
} else {
// Do we have correct count of values?
if (count($values) != $required_fields) {
// Hack for excel
if ($values[count($values) - 1] == ';') {
unset($values[count($values) - 1]);
} else {
$message = PMA\libraries\Message::error(
__(
'Invalid column count in CSV input'
. ' on line %d.'
)
);
$message->addParam($line);
$error = true;
break;
}
}
$first = true;
$sql = $sql_template;
foreach ($values as $key => $val) {
if (!$first) {
$sql .= ', ';
}
if ($val === null) {
$sql .= 'NULL';
} else {
$sql .= '\''
. $GLOBALS['dbi']->escapeString($val)
. '\'';
}
$first = false;
}
$sql .= ')';
if (isset($_POST['csv_replace'])) {
$sql .= " ON DUPLICATE KEY UPDATE ";
foreach ($fields as $field) {
$fieldName = PMA\libraries\Util::backquote(
$field['Field']
);
$sql .= $fieldName . " = VALUES(" . $fieldName
. "), ";
}
$sql = rtrim($sql, ', ');
}
/**
* @todo maybe we could add original line to verbose
* SQL in comment
*/
PMA_importRunQuery($sql, $sql, $sql_data);
}
$line++;
$csv_finish = false;
$values = array();
$buffer = mb_substr($buffer, $i + 1);
$len = mb_strlen($buffer);
$i = 0;
$lasti = -1;
$ch = mb_substr($buffer, 0, 1);
}
} // End of parser loop
} // End of import loop
if ($this->_getAnalyze()) {
/* Fill out all rows */
$num_rows = count($rows);
for ($i = 0; $i < $num_rows; ++$i) {
for ($j = count($rows[$i]); $j < $max_cols; ++$j) {
$rows[$i][] = 'NULL';
}
}
if (isset($_REQUEST['csv_col_names'])) {
$col_names = array_splice($rows, 0, 1);
$col_names = $col_names[0];
// MySQL column names can't end with a space character.
foreach ($col_names as $key => $col_name) {
$col_names[$key] = rtrim($col_name);
}
}
if ((isset($col_names) && count($col_names) != $max_cols)
|| !isset($col_names)
) {
// Fill out column names
for ($i = 0; $i < $max_cols; ++$i) {
$col_names[] = 'COL ' . ($i + 1);
}
}
if (mb_strlen($db)) {
$result = $GLOBALS['dbi']->fetchResult('SHOW TABLES');
$tbl_name = 'TABLE ' . (count($result) + 1);
} else {
$tbl_name = 'TBL_NAME';
}
$tables[] = array($tbl_name, $col_names, $rows);
/* Obtain the best-fit MySQL types for each column */
$analyses = array();
$analyses[] = PMA_analyzeTable($tables[0]);
/**
* string $db_name (no backquotes)
*
* array $table = array(table_name, array() column_names, array()() rows)
* array $tables = array of "$table"s
*
* array $analysis = array(array() column_types, array() column_sizes)
* array $analyses = array of "$analysis"s
*
* array $create = array of SQL strings
*
* array $options = an associative array of options
*/
/* Set database name to the currently selected one, if applicable */
list($db_name, $options) = $this->getDbnameAndOptions($db, 'CSV_DB');
/* Non-applicable parameters */
$create = null;
/* Created and execute necessary SQL statements from data */
PMA_buildSQL($db_name, $tables, $analyses, $create, $options, $sql_data);
unset($tables);
unset($analyses);
}
// Commit any possible data in buffers
PMA_importRunQuery('', '', $sql_data);
if (count($values) != 0 && !$error) {
$message = PMA\libraries\Message::error(
__('Invalid format of CSV input on line %d.')
);
$message->addParam($line);
$error = true;
}
}
/**
* Read the expected column_separated_with String of length
* $csv_terminated_len from the $buffer
* into variable $ch and return the read string $ch
*
* @param string $buffer The original string buffer read from
* csv file
* @param string $ch Partially read "column Separated with"
* string, also used to return after
* reading length equal $csv_terminated_len
* @param int $i Current read counter of buffer string
* @param int $csv_terminated_len The length of "column separated with"
* String
*
* @return string
*/
public function readCsvTerminatedString($buffer, $ch, $i, $csv_terminated_len)
{
for ($j = 0; $j < $csv_terminated_len - 1; $j++) {
$i++;
$ch .= mb_substr($buffer, $i, 1);
}
return $ch;
}
/* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
/**
* Returns true if the table should be analyzed, false otherwise
*
* @return bool
*/
private function _getAnalyze()
{
return $this->_analyze;
}
/**
* Sets to true if the table should be analyzed, false otherwise
*
* @param bool $analyze status
*
* @return void
*/
private function _setAnalyze($analyze)
{
$this->_analyze = $analyze;
}
}

View File

@ -0,0 +1,173 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* CSV import plugin for phpMyAdmin using LOAD DATA
*
* @package PhpMyAdmin-Import
* @subpackage LDI
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\options\items\BoolPropertyItem;
use PMA;
use PMA\libraries\plugins\import\AbstractImportCsv;
use PMA\libraries\properties\options\items\TextPropertyItem;
if (!defined('PHPMYADMIN')) {
exit;
}
// We need relations enabled and we work only on database
if ($GLOBALS['plugin_param'] !== 'table') {
$GLOBALS['skip_import'] = true;
return;
}
/**
* Handles the import for the CSV format using load data
*
* @package PhpMyAdmin-Import
* @subpackage LDI
*/
class ImportLdi extends AbstractImportCsv
{
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
if ($GLOBALS['cfg']['Import']['ldi_local_option'] == 'auto') {
$GLOBALS['cfg']['Import']['ldi_local_option'] = false;
$result = $GLOBALS['dbi']->tryQuery(
'SELECT @@local_infile;'
);
if ($result != false && $GLOBALS['dbi']->numRows($result) > 0) {
$tmp = $GLOBALS['dbi']->fetchRow($result);
if ($tmp[0] == 'ON') {
$GLOBALS['cfg']['Import']['ldi_local_option'] = true;
}
}
$GLOBALS['dbi']->freeResult($result);
unset($result);
}
$generalOptions = parent::setProperties();
$this->properties->setText('CSV using LOAD DATA');
$this->properties->setExtension('ldi');
$leaf = new TextPropertyItem(
"columns",
__('Column names: ')
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"ignore",
__('Do not abort on INSERT error')
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"local_option",
__('Use LOCAL keyword')
);
$generalOptions->addProperty($leaf);
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $finished, $import_file, $compression, $charset_conversion, $table;
global $ldi_local_option, $ldi_replace, $ldi_ignore, $ldi_terminated,
$ldi_enclosed, $ldi_escaped, $ldi_new_line, $skip_queries, $ldi_columns;
if ($import_file == 'none'
|| $compression != 'none'
|| $charset_conversion
) {
// We handle only some kind of data!
$GLOBALS['message'] = PMA\libraries\Message::error(
__('This plugin does not support compressed imports!')
);
$GLOBALS['error'] = true;
return;
}
$sql = 'LOAD DATA';
if (isset($ldi_local_option)) {
$sql .= ' LOCAL';
}
$sql .= ' INFILE \'' . $GLOBALS['dbi']->escapeString($import_file)
. '\'';
if (isset($ldi_replace)) {
$sql .= ' REPLACE';
} elseif (isset($ldi_ignore)) {
$sql .= ' IGNORE';
}
$sql .= ' INTO TABLE ' . PMA\libraries\Util::backquote($table);
if (strlen($ldi_terminated) > 0) {
$sql .= ' FIELDS TERMINATED BY \'' . $ldi_terminated . '\'';
}
if (strlen($ldi_enclosed) > 0) {
$sql .= ' ENCLOSED BY \''
. $GLOBALS['dbi']->escapeString($ldi_enclosed) . '\'';
}
if (strlen($ldi_escaped) > 0) {
$sql .= ' ESCAPED BY \''
. $GLOBALS['dbi']->escapeString($ldi_escaped) . '\'';
}
if (strlen($ldi_new_line) > 0) {
if ($ldi_new_line == 'auto') {
$ldi_new_line
= (PMA\libraries\Util::whichCrlf() == "\n")
? '\n'
: '\r\n';
}
$sql .= ' LINES TERMINATED BY \'' . $ldi_new_line . '\'';
}
if ($skip_queries > 0) {
$sql .= ' IGNORE ' . $skip_queries . ' LINES';
$skip_queries = 0;
}
if (strlen($ldi_columns) > 0) {
$sql .= ' (';
$tmp = preg_split('/,( ?)/', $ldi_columns);
$cnt_tmp = count($tmp);
for ($i = 0; $i < $cnt_tmp; $i++) {
if ($i > 0) {
$sql .= ', ';
}
/* Trim also `, if user already included backquoted fields */
$sql .= PMA\libraries\Util::backquote(
trim($tmp[$i], " \t\r\n\0\x0B`")
);
} // end for
$sql .= ')';
}
PMA_importRunQuery($sql, $sql, $sql_data);
PMA_importRunQuery('', '', $sql_data);
$finished = true;
}
}

View File

@ -0,0 +1,598 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* MediaWiki import plugin for phpMyAdmin
*
* @package PhpMyAdmin-Import
* @subpackage MediaWiki
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA;
use PMA\libraries\plugins\ImportPlugin;
/**
* Handles the import for the MediaWiki format
*
* @package PhpMyAdmin-Import
* @subpackage MediaWiki
*/
class ImportMediawiki extends ImportPlugin
{
/**
* Whether to analyze tables
*
* @var bool
*/
private $_analyze;
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$this->_setAnalyze(false);
if ($GLOBALS['plugin_param'] !== 'table') {
$this->_setAnalyze(true);
}
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setText(__('MediaWiki Table'));
$importPluginProperties->setExtension('txt');
$importPluginProperties->setMimeType('text/plain');
$importPluginProperties->setOptions(array());
$importPluginProperties->setOptionsText(__('Options'));
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $error, $timeout_passed, $finished;
// Defaults for parser
// The buffer that will be used to store chunks read from the imported file
$buffer = '';
// Used as storage for the last part of the current chunk data
// Will be appended to the first line of the next chunk, if there is one
$last_chunk_line = '';
// Remembers whether the current buffer line is part of a comment
$inside_comment = false;
// Remembers whether the current buffer line is part of a data comment
$inside_data_comment = false;
// Remembers whether the current buffer line is part of a structure comment
$inside_structure_comment = false;
// MediaWiki only accepts "\n" as row terminator
$mediawiki_new_line = "\n";
// Initialize the name of the current table
$cur_table_name = "";
while (!$finished && !$error && !$timeout_passed) {
$data = PMA_importGetNextChunk();
if ($data === false) {
// Subtract data we didn't handle yet and stop processing
$GLOBALS['offset'] -= mb_strlen($buffer);
break;
} elseif ($data === true) {
// Handle rest of buffer
} else {
// Append new data to buffer
$buffer = $data;
unset($data);
// Don't parse string if we're not at the end
// and don't have a new line inside
if (mb_strpos($buffer, $mediawiki_new_line) === false) {
continue;
}
}
// Because of reading chunk by chunk, the first line from the buffer
// contains only a portion of an actual line from the imported file.
// Therefore, we have to append it to the last line from the previous
// chunk. If we are at the first chunk, $last_chunk_line should be empty.
$buffer = $last_chunk_line . $buffer;
// Process the buffer line by line
$buffer_lines = explode($mediawiki_new_line, $buffer);
$full_buffer_lines_count = count($buffer_lines);
// If the reading is not finalised, the final line of the current chunk
// will not be complete
if (! $finished) {
$last_chunk_line = $buffer_lines[--$full_buffer_lines_count];
}
for ($line_nr = 0; $line_nr < $full_buffer_lines_count; ++$line_nr) {
$cur_buffer_line = trim($buffer_lines[$line_nr]);
// If the line is empty, go to the next one
if ($cur_buffer_line === '') {
continue;
}
$first_character = $cur_buffer_line[0];
$matches = array();
// Check beginning of comment
if (!strcmp(mb_substr($cur_buffer_line, 0, 4), "<!--")) {
$inside_comment = true;
continue;
} elseif ($inside_comment) {
// Check end of comment
if (!strcmp(mb_substr($cur_buffer_line, 0, 4), "-->")
) {
// Only data comments are closed. The structure comments
// will be closed when a data comment begins (in order to
// skip structure tables)
if ($inside_data_comment) {
$inside_data_comment = false;
}
// End comments that are not related to table structure
if (!$inside_structure_comment) {
$inside_comment = false;
}
} else {
// Check table name
$match_table_name = array();
if (preg_match(
"/^Table data for `(.*)`$/",
$cur_buffer_line,
$match_table_name
)
) {
$cur_table_name = $match_table_name[1];
$inside_data_comment = true;
$inside_structure_comment
= $this->_mngInsideStructComm(
$inside_structure_comment
);
} elseif (preg_match(
"/^Table structure for `(.*)`$/",
$cur_buffer_line,
$match_table_name
)
) {
// The structure comments will be ignored
$inside_structure_comment = true;
}
}
continue;
} elseif (preg_match('/^\{\|(.*)$/', $cur_buffer_line, $matches)) {
// Check start of table
// This will store all the column info on all rows from
// the current table read from the buffer
$cur_temp_table = array();
// Will be used as storage for the current row in the buffer
// Once all its columns are read, it will be added to
// $cur_temp_table and then it will be emptied
$cur_temp_line = array();
// Helps us differentiate the header columns
// from the normal columns
$in_table_header = false;
// End processing because the current line does not
// contain any column information
} elseif (mb_substr($cur_buffer_line, 0, 2) === '|-'
|| mb_substr($cur_buffer_line, 0, 2) === '|+'
|| mb_substr($cur_buffer_line, 0, 2) === '|}'
) {
// Check begin row or end table
// Add current line to the values storage
if (!empty($cur_temp_line)) {
// If the current line contains header cells
// ( marked with '!' ),
// it will be marked as table header
if ($in_table_header) {
// Set the header columns
$cur_temp_table_headers = $cur_temp_line;
} else {
// Normal line, add it to the table
$cur_temp_table [] = $cur_temp_line;
}
}
// Empty the temporary buffer
$cur_temp_line = array();
// No more processing required at the end of the table
if (mb_substr($cur_buffer_line, 0, 2) === '|}') {
$current_table = array(
$cur_table_name,
$cur_temp_table_headers,
$cur_temp_table,
);
// Import the current table data into the database
$this->_importDataOneTable($current_table, $sql_data);
// Reset table name
$cur_table_name = "";
}
// What's after the row tag is now only attributes
} elseif (($first_character === '|') || ($first_character === '!')) {
// Check cell elements
// Header cells
if ($first_character === '!') {
// Mark as table header, but treat as normal row
$cur_buffer_line = str_replace('!!', '||', $cur_buffer_line);
// Will be used to set $cur_temp_line as table header
$in_table_header = true;
} else {
$in_table_header = false;
}
// Loop through each table cell
$cells = $this->_explodeMarkup($cur_buffer_line);
foreach ($cells as $cell) {
$cell = $this->_getCellData($cell);
// Delete the beginning of the column, if there is one
$cell = trim($cell);
$col_start_chars = array("|", "!");
foreach ($col_start_chars as $col_start_char) {
$cell = $this->_getCellContent($cell, $col_start_char);
}
// Add the cell to the row
$cur_temp_line [] = $cell;
} // foreach $cells
} else {
// If it's none of the above, then the current line has a bad
// format
$message = PMA\libraries\Message::error(
__('Invalid format of mediawiki input on line: <br />%s.')
);
$message->addParam($cur_buffer_line);
$error = true;
}
} // End treating full buffer lines
} // while - finished parsing buffer
}
/**
* Imports data from a single table
*
* @param array $table containing all table info:
* <code>
* $table[0] - string containing table name
* $table[1] - array[] of table headers
* $table[2] - array[][] of table content rows
* </code>
*
* @param array &$sql_data 2-element array with sql data
*
* @global bool $analyze whether to scan for column types
*
* @return void
*/
private function _importDataOneTable($table, &$sql_data)
{
$analyze = $this->_getAnalyze();
if ($analyze) {
// Set the table name
$this->_setTableName($table[0]);
// Set generic names for table headers if they don't exist
$this->_setTableHeaders($table[1], $table[2][0]);
// Create the tables array to be used in PMA_buildSQL()
$tables = array();
$tables [] = array($table[0], $table[1], $table[2]);
// Obtain the best-fit MySQL types for each column
$analyses = array();
$analyses [] = PMA_analyzeTable($tables[0]);
$this->_executeImportTables($tables, $analyses, $sql_data);
}
// Commit any possible data in buffers
PMA_importRunQuery('', '', $sql_data);
}
/**
* Sets the table name
*
* @param string &$table_name reference to the name of the table
*
* @return void
*/
private function _setTableName(&$table_name)
{
if (empty($table_name)) {
$result = $GLOBALS['dbi']->fetchResult('SHOW TABLES');
// todo check if the name below already exists
$table_name = 'TABLE ' . (count($result) + 1);
}
}
/**
* Set generic names for table headers, if they don't exist
*
* @param array &$table_headers reference to the array containing the headers
* of a table
* @param array $table_row array containing the first content row
*
* @return void
*/
private function _setTableHeaders(&$table_headers, $table_row)
{
if (empty($table_headers)) {
// The first table row should contain the number of columns
// If they are not set, generic names will be given (COL 1, COL 2, etc)
$num_cols = count($table_row);
for ($i = 0; $i < $num_cols; ++$i) {
$table_headers [$i] = 'COL ' . ($i + 1);
}
}
}
/**
* Sets the database name and additional options and calls PMA_buildSQL()
* Used in PMA_importDataAllTables() and $this->_importDataOneTable()
*
* @param array &$tables structure:
* array(
* array(table_name, array() column_names, array()()
* rows)
* )
* @param array &$analyses structure:
* $analyses = array(
* array(array() column_types, array() column_sizes)
* )
* @param array &$sql_data 2-element array with sql data
*
* @global string $db name of the database to import in
*
* @return void
*/
private function _executeImportTables(&$tables, &$analyses, &$sql_data)
{
global $db;
// $db_name : The currently selected database name, if applicable
// No backquotes
// $options : An associative array of options
list($db_name, $options) = $this->getDbnameAndOptions($db, 'mediawiki_DB');
// Array of SQL strings
// Non-applicable parameters
$create = null;
// Create and execute necessary SQL statements from data
PMA_buildSQL($db_name, $tables, $analyses, $create, $options, $sql_data);
unset($tables);
unset($analyses);
}
/**
* Replaces all instances of the '||' separator between delimiters
* in a given string
*
* @param string $replace the string to be replaced with
* @param string $subject the text to be replaced
*
* @return string with replacements
*/
private function _delimiterReplace($replace, $subject)
{
// String that will be returned
$cleaned = "";
// Possible states of current character
$inside_tag = false;
$inside_attribute = false;
// Attributes can be declared with either " or '
$start_attribute_character = false;
// The full separator is "||";
// This remembers if the previous character was '|'
$partial_separator = false;
// Parse text char by char
for ($i = 0; $i < strlen($subject); $i++) {
$cur_char = $subject[$i];
// Check for separators
if ($cur_char == '|') {
// If we're not inside a tag, then this is part of a real separator,
// so we append it to the current segment
if (!$inside_attribute) {
$cleaned .= $cur_char;
if ($partial_separator) {
$inside_tag = false;
$inside_attribute = false;
}
} elseif ($partial_separator) {
// If we are inside a tag, we replace the current char with
// the placeholder and append that to the current segment
$cleaned .= $replace;
}
// If the previous character was also '|', then this ends a
// full separator. If not, this may be the beginning of one
$partial_separator = !$partial_separator;
} else {
// If we're inside a tag attribute and the current character is
// not '|', but the previous one was, it means that the single '|'
// was not appended, so we append it now
if ($partial_separator && $inside_attribute) {
$cleaned .= "|";
}
// If the char is different from "|", no separator can be formed
$partial_separator = false;
// any other character should be appended to the current segment
$cleaned .= $cur_char;
if ($cur_char == '<' && !$inside_attribute) {
// start of a tag
$inside_tag = true;
} elseif ($cur_char == '>' && !$inside_attribute) {
// end of a tag
$inside_tag = false;
} elseif (($cur_char == '"' || $cur_char == "'") && $inside_tag) {
// start or end of an attribute
if (!$inside_attribute) {
$inside_attribute = true;
// remember the attribute`s declaration character (" or ')
$start_attribute_character = $cur_char;
} else {
if ($cur_char == $start_attribute_character) {
$inside_attribute = false;
// unset attribute declaration character
$start_attribute_character = false;
}
}
}
}
} // end for each character in $subject
return $cleaned;
}
/**
* Separates a string into items, similarly to explode
* Uses the '||' separator (which is standard in the mediawiki format)
* and ignores any instances of it inside markup tags
* Used in parsing buffer lines containing data cells
*
* @param string $text text to be split
*
* @return array
*/
private function _explodeMarkup($text)
{
$separator = "||";
$placeholder = "\x00";
// Remove placeholder instances
$text = str_replace($placeholder, '', $text);
// Replace instances of the separator inside HTML-like
// tags with the placeholder
$cleaned = $this->_delimiterReplace($placeholder, $text);
// Explode, then put the replaced separators back in
$items = explode($separator, $cleaned);
foreach ($items as $i => $str) {
$items[$i] = str_replace($placeholder, $separator, $str);
}
return $items;
}
/* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
/**
* Returns true if the table should be analyzed, false otherwise
*
* @return bool
*/
private function _getAnalyze()
{
return $this->_analyze;
}
/**
* Sets to true if the table should be analyzed, false otherwise
*
* @param bool $analyze status
*
* @return void
*/
private function _setAnalyze($analyze)
{
$this->_analyze = $analyze;
}
/**
* Get cell
*
* @param string $cell Cell
*
* @return mixed
*/
private function _getCellData($cell)
{
// A cell could contain both parameters and data
$cell_data = explode('|', $cell, 2);
// A '|' inside an invalid link should not
// be mistaken as delimiting cell parameters
if (mb_strpos($cell_data[0], '[[') === false) {
return $cell;
}
if (count($cell_data) == 1) {
return $cell_data[0];
}
return $cell_data[1];
}
/**
* Manage $inside_structure_comment
*
* @param boolean $inside_structure_comment Value to test
*
* @return bool
*/
private function _mngInsideStructComm($inside_structure_comment)
{
// End ignoring structure rows
if ($inside_structure_comment) {
$inside_structure_comment = false;
}
return $inside_structure_comment;
}
/**
* Get cell content
*
* @param string $cell Cell
* @param string $col_start_char Start char
*
* @return string
*/
private function _getCellContent($cell, $col_start_char)
{
if (mb_strpos($cell, $col_start_char) === 0) {
$cell = trim(mb_substr($cell, 1));
}
return $cell;
}
}

View File

@ -0,0 +1,430 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* OpenDocument Spreadsheet import plugin for phpMyAdmin
*
* @todo Pretty much everything
* @todo Importing of accented characters seems to fail
* @package PhpMyAdmin-Import
* @subpackage ODS
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\options\items\BoolPropertyItem;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA\libraries\properties\options\groups\OptionsPropertyMainGroup;
use PMA\libraries\properties\options\groups\OptionsPropertyRootGroup;
use PMA;
use PMA\libraries\plugins\ImportPlugin;
use SimpleXMLElement;
/**
* We need way to disable external XML entities processing.
*/
if (!function_exists('libxml_disable_entity_loader')) {
$GLOBALS['skip_import'] = true;
return;
}
/**
* Handles the import for the ODS format
*
* @package PhpMyAdmin-Import
* @subpackage ODS
*/
class ImportOds extends ImportPlugin
{
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setText('OpenDocument Spreadsheet');
$importPluginProperties->setExtension('ods');
$importPluginProperties->setOptionsText(__('Options'));
// create the root group that will be the options field for
// $importPluginProperties
// this will be shown as "Format specific options"
$importSpecificOptions = new OptionsPropertyRootGroup(
"Format Specific Options"
);
// general options main group
$generalOptions = new OptionsPropertyMainGroup("general_opts");
// create primary items and add them to the group
$leaf = new BoolPropertyItem(
"col_names",
__(
'The first line of the file contains the table column names'
. ' <i>(if this is unchecked, the first line will become part'
. ' of the data)</i>'
)
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"empty_rows",
__('Do not import empty rows')
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"recognize_percentages",
__(
'Import percentages as proper decimals <i>(ex. 12.00% to .12)</i>'
)
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"recognize_currency",
__('Import currencies <i>(ex. $5.00 to 5.00)</i>')
);
$generalOptions->addProperty($leaf);
// add the main group to the root group
$importSpecificOptions->addProperty($generalOptions);
// set the options for the import plugin property item
$importPluginProperties->setOptions($importSpecificOptions);
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $db, $error, $timeout_passed, $finished;
$i = 0;
$len = 0;
$buffer = "";
/**
* Read in the file via PMA_importGetNextChunk so that
* it can process compressed files
*/
while (!($finished && $i >= $len) && !$error && !$timeout_passed) {
$data = PMA_importGetNextChunk();
if ($data === false) {
/* subtract data we didn't handle yet and stop processing */
$GLOBALS['offset'] -= strlen($buffer);
break;
} elseif ($data === true) {
/* Handle rest of buffer */
} else {
/* Append new data to buffer */
$buffer .= $data;
unset($data);
}
}
unset($data);
/**
* Disable loading of external XML entities.
*/
libxml_disable_entity_loader();
/**
* Load the XML string
*
* The option LIBXML_COMPACT is specified because it can
* result in increased performance without the need to
* alter the code in any way. It's basically a freebee.
*/
$xml = @simplexml_load_string($buffer, "SimpleXMLElement", LIBXML_COMPACT);
unset($buffer);
if ($xml === false) {
$sheets = array();
$GLOBALS['message'] = PMA\libraries\Message::error(
__(
'The XML file specified was either malformed or incomplete.'
. ' Please correct the issue and try again.'
)
);
$GLOBALS['error'] = true;
} else {
/** @var SimpleXMLElement $root */
$root = $xml->children('office', true)->{'body'}->{'spreadsheet'};
if (empty($root)) {
$sheets = array();
$GLOBALS['message'] = PMA\libraries\Message::error(
__('Could not parse OpenDocument Spreadsheet!')
);
$GLOBALS['error'] = true;
} else {
$sheets = $root->children('table', true);
}
}
$tables = array();
$max_cols = 0;
$col_count = 0;
$col_names = array();
$tempRow = array();
$tempRows = array();
$rows = array();
/* Iterate over tables */
/** @var SimpleXMLElement $sheet */
foreach ($sheets as $sheet) {
$col_names_in_first_row = isset($_REQUEST['ods_col_names']);
/* Iterate over rows */
/** @var SimpleXMLElement $row */
foreach ($sheet as $row) {
$type = $row->getName();
if (strcmp('table-row', $type)) {
continue;
}
/* Iterate over columns */
$cellCount = count($row);
$a = 0;
/** @var SimpleXMLElement $cell */
foreach ($row as $cell) {
$a++;
$text = $cell->children('text', true);
$cell_attrs = $cell->attributes('office', true);
if (count($text) != 0) {
$attr = $cell->attributes('table', true);
$num_repeat = (int)$attr['number-columns-repeated'];
$num_iterations = $num_repeat ? $num_repeat : 1;
for ($k = 0; $k < $num_iterations; $k++) {
$value = $this->getValue($cell_attrs, $text);
if (!$col_names_in_first_row) {
$tempRow[] = $value;
} else {
// MySQL column names can't end with a space
// character.
$col_names[] = rtrim($value);
}
++$col_count;
}
continue;
}
// skip empty repeats in the last row
if ($a == $cellCount) {
continue;
}
$attr = $cell->attributes('table', true);
$num_null = (int)$attr['number-columns-repeated'];
if ($num_null) {
if (!$col_names_in_first_row) {
for ($i = 0; $i < $num_null; ++$i) {
$tempRow[] = 'NULL';
++$col_count;
}
} else {
for ($i = 0; $i < $num_null; ++$i) {
$col_names[] = PMA_getColumnAlphaName(
$col_count + 1
);
++$col_count;
}
}
} else {
if (!$col_names_in_first_row) {
$tempRow[] = 'NULL';
} else {
$col_names[] = PMA_getColumnAlphaName(
$col_count + 1
);
}
++$col_count;
}
} //Endforeach
/* Find the widest row */
if ($col_count > $max_cols) {
$max_cols = $col_count;
}
/* Don't include a row that is full of NULL values */
if (!$col_names_in_first_row) {
if ($_REQUEST['ods_empty_rows']) {
foreach ($tempRow as $cell) {
if (strcmp('NULL', $cell)) {
$tempRows[] = $tempRow;
break;
}
}
} else {
$tempRows[] = $tempRow;
}
}
$col_count = 0;
$col_names_in_first_row = false;
$tempRow = array();
}
/* Skip over empty sheets */
if (count($tempRows) == 0 || count($tempRows[0]) == 0) {
$col_names = array();
$tempRow = array();
$tempRows = array();
continue;
}
/**
* Fill out each row as necessary to make
* every one exactly as wide as the widest
* row. This included column names.
*/
/* Fill out column names */
for ($i = count($col_names); $i < $max_cols; ++$i) {
$col_names[] = PMA_getColumnAlphaName($i + 1);
}
/* Fill out all rows */
$num_rows = count($tempRows);
for ($i = 0; $i < $num_rows; ++$i) {
for ($j = count($tempRows[$i]); $j < $max_cols; ++$j) {
$tempRows[$i][] = 'NULL';
}
}
/* Store the table name so we know where to place the row set */
$tbl_attr = $sheet->attributes('table', true);
$tables[] = array((string)$tbl_attr['name']);
/* Store the current sheet in the accumulator */
$rows[] = array((string)$tbl_attr['name'], $col_names, $tempRows);
$tempRows = array();
$col_names = array();
$max_cols = 0;
}
unset($tempRow);
unset($tempRows);
unset($col_names);
unset($sheets);
unset($xml);
/**
* Bring accumulated rows into the corresponding table
*/
$num_tables = count($tables);
for ($i = 0; $i < $num_tables; ++$i) {
$num_rows = count($rows);
for ($j = 0; $j < $num_rows; ++$j) {
if (strcmp($tables[$i][TBL_NAME], $rows[$j][TBL_NAME])) {
continue;
}
if (!isset($tables[$i][COL_NAMES])) {
$tables[$i][] = $rows[$j][COL_NAMES];
}
$tables[$i][ROWS] = $rows[$j][ROWS];
}
}
/* No longer needed */
unset($rows);
/* Obtain the best-fit MySQL types for each column */
$analyses = array();
$len = count($tables);
for ($i = 0; $i < $len; ++$i) {
$analyses[] = PMA_analyzeTable($tables[$i]);
}
/**
* string $db_name (no backquotes)
*
* array $table = array(table_name, array() column_names, array()() rows)
* array $tables = array of "$table"s
*
* array $analysis = array(array() column_types, array() column_sizes)
* array $analyses = array of "$analysis"s
*
* array $create = array of SQL strings
*
* array $options = an associative array of options
*/
/* Set database name to the currently selected one, if applicable */
list($db_name, $options) = $this->getDbnameAndOptions($db, 'ODS_DB');
/* Non-applicable parameters */
$create = null;
/* Created and execute necessary SQL statements from data */
PMA_buildSQL($db_name, $tables, $analyses, $create, $options, $sql_data);
unset($tables);
unset($analyses);
/* Commit any possible data in buffers */
PMA_importRunQuery('', '', $sql_data);
}
/**
* Get value
*
* @param array $cell_attrs Cell attributes
* @param array $text Texts
*
* @return float|string
*/
protected function getValue($cell_attrs, $text)
{
if ($_REQUEST['ods_recognize_percentages']
&& !strcmp(
'percentage',
$cell_attrs['value-type']
)
) {
$value = (double)$cell_attrs['value'];
return $value;
} elseif ($_REQUEST['ods_recognize_currency']
&& !strcmp('currency', $cell_attrs['value-type'])
) {
$value = (double)$cell_attrs['value'];
return $value;
} else {
/* We need to concatenate all paragraphs */
$values = array();
foreach ($text as $paragraph) {
$values[] = (string)$paragraph;
}
$value = implode("\n", $values);
return $value;
}
}
}

View File

@ -0,0 +1,333 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* ESRI Shape file import plugin for phpMyAdmin
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA;
use PMA\libraries\plugins\ImportPlugin;
use PMA\libraries\gis\GISFactory;
use PMA\libraries\gis\GISMultilinestring;
use PMA\libraries\gis\GISMultipoint;
use PMA\libraries\gis\GISPoint;
use PMA\libraries\gis\GISPolygon;
use PMA\libraries\plugins\import\ShapeFile;
/* Get the ShapeFile class */
require_once 'libraries/bfShapeFiles/ShapeFile.lib.php';
/**
* Handles the import for ESRI Shape files
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
class ImportShp extends ImportPlugin
{
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setText(__('ESRI Shape File'));
$importPluginProperties->setExtension('shp');
$importPluginProperties->setOptions(array());
$importPluginProperties->setOptionsText(__('Options'));
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $db, $error, $finished, $compression,
$import_file, $local_import_file, $message;
$GLOBALS['finished'] = false;
$shp = new ShapeFile(1);
// If the zip archive has more than one file,
// get the correct content to the buffer from .shp file.
if ($compression == 'application/zip'
&& PMA_getNoOfFilesInZip($import_file) > 1
) {
$zip_content = PMA_getZipContents($import_file, '/^.*\.shp$/i');
$GLOBALS['import_text'] = $zip_content['data'];
}
$temp_dbf_file = false;
// We need dbase extension to handle .dbf file
if (extension_loaded('dbase')) {
// If we can extract the zip archive to 'TempDir'
// and use the files in it for import
if ($compression == 'application/zip'
&& !empty($GLOBALS['cfg']['TempDir'])
&& @is_writable($GLOBALS['cfg']['TempDir'])
) {
$dbf_file_name = PMA_findFileFromZipArchive(
'/^.*\.dbf$/i',
$import_file
);
// If the corresponding .dbf file is in the zip archive
if ($dbf_file_name) {
// Extract the .dbf file and point to it.
$extracted = PMA_zipExtract(
$import_file,
$dbf_file_name
);
if ($extracted !== false) {
$dbf_file_path = realpath($GLOBALS['cfg']['TempDir'])
. (PMA_IS_WINDOWS ? '\\' : '/')
. PMA_sanitizeFilename($dbf_file_name, true);
$handle = fopen($dbf_file_path, 'wb');
if ($handle !== false) {
fwrite($handle, $extracted);
fclose($handle);
$temp_dbf_file = true;
// Replace the .dbf with .*, as required
// by the bsShapeFiles library.
$file_name = substr(
$dbf_file_path, 0, strlen($dbf_file_path) - 4
) . '.*';
$shp->FileName = $file_name;
}
}
}
} elseif (!empty($local_import_file)
&& !empty($GLOBALS['cfg']['UploadDir'])
&& $compression == 'none'
) {
// If file is in UploadDir, use .dbf file in the same UploadDir
// to load extra data.
// Replace the .shp with .*,
// so the bsShapeFiles library correctly locates .dbf file.
$file_name = mb_substr(
$import_file,
0,
mb_strlen($import_file) - 4
) . '.*';
$shp->FileName = $file_name;
}
}
// Delete the .dbf file extracted to 'TempDir'
if ($temp_dbf_file
&& isset($dbf_file_path)
&& file_exists($dbf_file_path)
) {
unlink($dbf_file_path);
}
// Load data
$shp->loadFromFile('');
if ($shp->lastError != "") {
$error = true;
$message = PMA\libraries\Message::error(
__('There was an error importing the ESRI shape file: "%s".')
);
$message->addParam($shp->lastError);
return;
}
$esri_types = array(
0 => 'Null Shape',
1 => 'Point',
3 => 'PolyLine',
5 => 'Polygon',
8 => 'MultiPoint',
11 => 'PointZ',
13 => 'PolyLineZ',
15 => 'PolygonZ',
18 => 'MultiPointZ',
21 => 'PointM',
23 => 'PolyLineM',
25 => 'PolygonM',
28 => 'MultiPointM',
31 => 'MultiPatch',
);
switch ($shp->shapeType) {
// ESRI Null Shape
case 0:
break;
// ESRI Point
case 1:
$gis_type = 'point';
break;
// ESRI PolyLine
case 3:
$gis_type = 'multilinestring';
break;
// ESRI Polygon
case 5:
$gis_type = 'multipolygon';
break;
// ESRI MultiPoint
case 8:
$gis_type = 'multipoint';
break;
default:
$error = true;
if (!isset($esri_types[$shp->shapeType])) {
$message = PMA\libraries\Message::error(
__(
'You tried to import an invalid file or the imported file'
. ' contains invalid data!'
)
);
} else {
$message = PMA\libraries\Message::error(
__('MySQL Spatial Extension does not support ESRI type "%s".')
);
$message->addParam($esri_types[$shp->shapeType]);
}
return;
}
if (isset($gis_type)) {
/** @var GISMultilinestring|\PMA\libraries\gis\GISMultipoint|\PMA\libraries\gis\GISPoint|GISPolygon $gis_obj */
$gis_obj = GISFactory::factory($gis_type);
} else {
$gis_obj = null;
}
$num_rows = count($shp->records);
// If .dbf file is loaded, the number of extra data columns
$num_data_cols = isset($shp->DBFHeader) ? count($shp->DBFHeader) : 0;
$rows = array();
$col_names = array();
if ($num_rows != 0) {
foreach ($shp->records as $record) {
$tempRow = array();
if ($gis_obj == null) {
$tempRow[] = null;
} else {
$tempRow[] = "GeomFromText('"
. $gis_obj->getShape($record->SHPData) . "')";
}
if (isset($shp->DBFHeader)) {
foreach ($shp->DBFHeader as $c) {
$cell = trim($record->DBFData[$c[0]]);
if (!strcmp($cell, '')) {
$cell = 'NULL';
}
$tempRow[] = $cell;
}
}
$rows[] = $tempRow;
}
}
if (count($rows) == 0) {
$error = true;
$message = PMA\libraries\Message::error(
__('The imported file does not contain any data!')
);
return;
}
// Column names for spatial column and the rest of the columns,
// if they are available
$col_names[] = 'SPATIAL';
for ($n = 0; $n < $num_data_cols; $n++) {
$col_names[] = $shp->DBFHeader[$n][0];
}
// Set table name based on the number of tables
if (mb_strlen($db)) {
$result = $GLOBALS['dbi']->fetchResult('SHOW TABLES');
$table_name = 'TABLE ' . (count($result) + 1);
} else {
$table_name = 'TBL_NAME';
}
$tables = array(array($table_name, $col_names, $rows));
// Use data from shape file to chose best-fit MySQL types for each column
$analyses = array();
$analyses[] = PMA_analyzeTable($tables[0]);
$table_no = 0;
$spatial_col = 0;
$analyses[$table_no][TYPES][$spatial_col] = GEOMETRY;
$analyses[$table_no][FORMATTEDSQL][$spatial_col] = true;
// Set database name to the currently selected one, if applicable
if (mb_strlen($db)) {
$db_name = $db;
$options = array('create_db' => false);
} else {
$db_name = 'SHP_DB';
$options = null;
}
// Created and execute necessary SQL statements from data
$null_param = null;
PMA_buildSQL($db_name, $tables, $analyses, $null_param, $options, $sql_data);
unset($tables);
unset($analyses);
$finished = true;
$error = false;
// Commit any possible data in buffers
PMA_importRunQuery('', '', $sql_data);
}
/**
* Returns specified number of bytes from the buffer.
* Buffer automatically fetches next chunk of data when the buffer
* falls short.
* Sets $eof when $GLOBALS['finished'] is set and the buffer falls short.
*
* @param int $length number of bytes
*
* @return string
*/
public static function readFromBuffer($length)
{
global $buffer, $eof;
if (strlen($buffer) < $length) {
if ($GLOBALS['finished']) {
$eof = true;
} else {
$buffer .= PMA_importGetNextChunk();
}
}
$result = substr($buffer, 0, $length);
$buffer = substr($buffer, $length);
return $result;
}
}

View File

@ -0,0 +1,198 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* SQL import plugin for phpMyAdmin
*
* @package PhpMyAdmin-Import
* @subpackage SQL
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\options\items\BoolPropertyItem;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA\libraries\properties\options\groups\OptionsPropertyMainGroup;
use PMA\libraries\properties\options\groups\OptionsPropertyRootGroup;
use PMA;
use PMA\libraries\plugins\ImportPlugin;
use PMA\libraries\properties\options\items\SelectPropertyItem;
use SqlParser;
/**
* Handles the import for the SQL format
*
* @package PhpMyAdmin-Import
* @subpackage SQL
*/
class ImportSql extends ImportPlugin
{
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setText('SQL');
$importPluginProperties->setExtension('sql');
$importPluginProperties->setOptionsText(__('Options'));
$compats = $GLOBALS['dbi']->getCompatibilities();
if (count($compats) > 0) {
$values = array();
foreach ($compats as $val) {
$values[$val] = $val;
}
// create the root group that will be the options field for
// $importPluginProperties
// this will be shown as "Format specific options"
$importSpecificOptions = new OptionsPropertyRootGroup(
"Format Specific Options"
);
// general options main group
$generalOptions = new OptionsPropertyMainGroup("general_opts");
// create primary items and add them to the group
$leaf = new SelectPropertyItem(
"compatibility",
__('SQL compatibility mode:')
);
$leaf->setValues($values);
$leaf->setDoc(
array(
'manual_MySQL_Database_Administration',
'Server_SQL_mode',
)
);
$generalOptions->addProperty($leaf);
$leaf = new BoolPropertyItem(
"no_auto_value_on_zero",
__('Do not use <code>AUTO_INCREMENT</code> for zero values')
);
$leaf->setDoc(
array(
'manual_MySQL_Database_Administration',
'Server_SQL_mode',
'sqlmode_no_auto_value_on_zero',
)
);
$generalOptions->addProperty($leaf);
// add the main group to the root group
$importSpecificOptions->addProperty($generalOptions);
// set the options for the import plugin property item
$importPluginProperties->setOptions($importSpecificOptions);
}
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $error, $timeout_passed;
// Handle compatibility options.
$this->_setSQLMode($GLOBALS['dbi'], $_REQUEST);
$bq = new SqlParser\Utils\BufferedQuery();
if (isset($_POST['sql_delimiter'])) {
$bq->setDelimiter($_POST['sql_delimiter']);
}
/**
* Will be set in PMA_importGetNextChunk().
*
* @global bool $GLOBALS ['finished']
*/
$GLOBALS['finished'] = false;
while ((!$error) && (!$timeout_passed)) {
// Getting the first statement, the remaining data and the last
// delimiter.
$statement = $bq->extract();
// If there is no full statement, we are looking for more data.
if (empty($statement)) {
// Importing new data.
$newData = PMA_importGetNextChunk();
// Subtract data we didn't handle yet and stop processing.
if ($newData === false) {
$GLOBALS['offset'] -= mb_strlen($bq->query);
break;
}
// Checking if the input buffer has finished.
if ($newData === true) {
$GLOBALS['finished'] = true;
break;
}
// Convert CR (but not CRLF) to LF otherwise all queries may
// not get executed on some platforms.
$bq->query .= preg_replace("/\r($|[^\n])/", "\n$1", $newData);
continue;
}
// Executing the query.
PMA_importRunQuery($statement, $statement, $sql_data);
}
// Extracting remaining statements.
while ((!$error) && (!$timeout_passed) && (!empty($bq->query))) {
$statement = $bq->extract(true);
if (!empty($statement)) {
PMA_importRunQuery($statement, $statement, $sql_data);
}
}
// Finishing.
PMA_importRunQuery('', '', $sql_data);
}
/**
* Handle compatibility options
*
* @param PMA\libraries\DatabaseInterface $dbi Database interface
* @param array $request Request array
*
* @return void
*/
private function _setSQLMode($dbi, $request)
{
$sql_modes = array();
if (isset($request['sql_compatibility'])
&& 'NONE' != $request['sql_compatibility']
) {
$sql_modes[] = $request['sql_compatibility'];
}
if (isset($request['sql_no_auto_value_on_zero'])) {
$sql_modes[] = 'NO_AUTO_VALUE_ON_ZERO';
}
if (count($sql_modes) > 0) {
$dbi->tryQuery(
'SET SQL_MODE="' . implode(',', $sql_modes) . '"'
);
}
}
}

View File

@ -0,0 +1,378 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* XML import plugin for phpMyAdmin
*
* @todo Improve efficiency
* @package PhpMyAdmin-Import
* @subpackage XML
*/
namespace PMA\libraries\plugins\import;
use PMA\libraries\properties\plugins\ImportPluginProperties;
use PMA;
use PMA\libraries\plugins\ImportPlugin;
use SimpleXMLElement;
/**
* We need way to disable external XML entities processing.
*/
if (!function_exists('libxml_disable_entity_loader')) {
$GLOBALS['skip_import'] = true;
return;
}
/**
* Handles the import for the XML format
*
* @package PhpMyAdmin-Import
* @subpackage XML
*/
class ImportXml extends ImportPlugin
{
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$importPluginProperties = new ImportPluginProperties();
$importPluginProperties->setText(__('XML'));
$importPluginProperties->setExtension('xml');
$importPluginProperties->setMimeType('text/xml');
$importPluginProperties->setOptions(array());
$importPluginProperties->setOptionsText(__('Options'));
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
global $error, $timeout_passed, $finished, $db;
$i = 0;
$len = 0;
$buffer = "";
/**
* Read in the file via PMA_importGetNextChunk so that
* it can process compressed files
*/
while (!($finished && $i >= $len) && !$error && !$timeout_passed) {
$data = PMA_importGetNextChunk();
if ($data === false) {
/* subtract data we didn't handle yet and stop processing */
$GLOBALS['offset'] -= strlen($buffer);
break;
} elseif ($data === true) {
/* Handle rest of buffer */
} else {
/* Append new data to buffer */
$buffer .= $data;
unset($data);
}
}
unset($data);
/**
* Disable loading of external XML entities.
*/
libxml_disable_entity_loader();
/**
* Load the XML string
*
* The option LIBXML_COMPACT is specified because it can
* result in increased performance without the need to
* alter the code in any way. It's basically a freebee.
*/
$xml = @simplexml_load_string($buffer, "SimpleXMLElement", LIBXML_COMPACT);
unset($buffer);
/**
* The XML was malformed
*/
if ($xml === false) {
PMA\libraries\Message::error(
__(
'The XML file specified was either malformed or incomplete.'
. ' Please correct the issue and try again.'
)
)
->display();
unset($xml);
$GLOBALS['finished'] = false;
return;
}
/**
* Table accumulator
*/
$tables = array();
/**
* Row accumulator
*/
$rows = array();
/**
* Temp arrays
*/
$tempRow = array();
$tempCells = array();
/**
* CREATE code included (by default: no)
*/
$struct_present = false;
/**
* Analyze the data in each table
*/
$namespaces = $xml->getNameSpaces(true);
/**
* Get the database name, collation and charset
*/
$db_attr = $xml->children($namespaces['pma'])
->{'structure_schemas'}->{'database'};
if ($db_attr instanceof SimpleXMLElement) {
$db_attr = $db_attr->attributes();
$db_name = (string)$db_attr['name'];
$collation = (string)$db_attr['collation'];
$charset = (string)$db_attr['charset'];
} else {
/**
* If the structure section is not present
* get the database name from the data section
*/
$db_attr = $xml->children()
->attributes();
$db_name = (string)$db_attr['name'];
$collation = null;
$charset = null;
}
/**
* The XML was malformed
*/
if ($db_name === null) {
PMA\libraries\Message::error(
__(
'The XML file specified was either malformed or incomplete.'
. ' Please correct the issue and try again.'
)
)
->display();
unset($xml);
$GLOBALS['finished'] = false;
return;
}
/**
* Retrieve the structure information
*/
if (isset($namespaces['pma'])) {
/**
* Get structures for all tables
*
* @var SimpleXMLElement $struct
*/
$struct = $xml->children($namespaces['pma']);
$create = array();
/** @var SimpleXMLElement $val1 */
foreach ($struct as $val1) {
/** @var SimpleXMLElement $val2 */
foreach ($val1 as $val2) {
// Need to select the correct database for the creation of
// tables, views, triggers, etc.
/**
* @todo Generating a USE here blocks importing of a table
* into another database.
*/
$attrs = $val2->attributes();
$create[] = "USE "
. PMA\libraries\Util::backquote(
$attrs["name"]
);
foreach ($val2 as $val3) {
/**
* Remove the extra cosmetic spacing
*/
$val3 = str_replace(" ", "", (string)$val3);
$create[] = $val3;
}
}
}
$struct_present = true;
}
/**
* Move down the XML tree to the actual data
*/
$xml = $xml->children()
->children();
$data_present = false;
/**
* Only attempt to analyze/collect data if there is data present
*/
if ($xml && @count($xml->children())) {
$data_present = true;
/**
* Process all database content
*/
foreach ($xml as $v1) {
$tbl_attr = $v1->attributes();
$isInTables = false;
$num_tables = count($tables);
for ($i = 0; $i < $num_tables; ++$i) {
if (!strcmp($tables[$i][TBL_NAME], (string)$tbl_attr['name'])) {
$isInTables = true;
break;
}
}
if (!$isInTables) {
$tables[] = array((string)$tbl_attr['name']);
}
foreach ($v1 as $v2) {
$row_attr = $v2->attributes();
if (!array_search((string)$row_attr['name'], $tempRow)) {
$tempRow[] = (string)$row_attr['name'];
}
$tempCells[] = (string)$v2;
}
$rows[] = array((string)$tbl_attr['name'], $tempRow, $tempCells);
$tempRow = array();
$tempCells = array();
}
unset($tempRow);
unset($tempCells);
unset($xml);
/**
* Bring accumulated rows into the corresponding table
*/
$num_tables = count($tables);
for ($i = 0; $i < $num_tables; ++$i) {
$num_rows = count($rows);
for ($j = 0; $j < $num_rows; ++$j) {
if (!strcmp($tables[$i][TBL_NAME], $rows[$j][TBL_NAME])) {
if (!isset($tables[$i][COL_NAMES])) {
$tables[$i][] = $rows[$j][COL_NAMES];
}
$tables[$i][ROWS][] = $rows[$j][ROWS];
}
}
}
unset($rows);
if (!$struct_present) {
$analyses = array();
$len = count($tables);
for ($i = 0; $i < $len; ++$i) {
$analyses[] = PMA_analyzeTable($tables[$i]);
}
}
}
unset($xml);
unset($tempCells);
unset($rows);
/**
* Only build SQL from data if there is data present
*/
if ($data_present) {
/**
* Set values to NULL if they were not present
* to maintain PMA_buildSQL() call integrity
*/
if (!isset($analyses)) {
$analyses = null;
if (!$struct_present) {
$create = null;
}
}
}
/**
* string $db_name (no backquotes)
*
* array $table = array(table_name, array() column_names, array()() rows)
* array $tables = array of "$table"s
*
* array $analysis = array(array() column_types, array() column_sizes)
* array $analyses = array of "$analysis"s
*
* array $create = array of SQL strings
*
* array $options = an associative array of options
*/
/* Set database name to the currently selected one, if applicable */
if (strlen($db)) {
/* Override the database name in the XML file, if one is selected */
$db_name = $db;
$options = array('create_db' => false);
} else {
if ($db_name === null) {
$db_name = 'XML_DB';
}
/* Set database collation/charset */
$options = array(
'db_collation' => $collation,
'db_charset' => $charset,
);
}
/* Created and execute necessary SQL statements from data */
PMA_buildSQL($db_name, $tables, $analyses, $create, $options, $sql_data);
unset($analyses);
unset($tables);
unset($create);
/* Commit any possible data in buffers */
PMA_importRunQuery('', '', $sql_data);
}
}

View File

@ -0,0 +1,155 @@
This directory holds import plugins for phpMyAdmin. Any new plugin should
basically follow the structure presented here. The messages must use our
gettext mechanism, see https://wiki.phpmyadmin.net/pma/Gettext_for_developers.
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* [Name] import plugin for phpMyAdmin
*
* @package PhpMyAdmin-Import
* @subpackage [Name]
*/
if (! defined('PHPMYADMIN')) {
exit;
}
/* Get the import interface */
require_once 'libraries/plugins/PMA\libraries\plugins\ImportPlugin.class.php';
/**
* Handles the import for the [Name] format
*
* @package PhpMyAdmin-Import
*/
class Import[Name] extends PMA\libraries\plugins\ImportPlugin
{
/**
* optional - declare variables and descriptions
*
* @var type
*/
private $_myOptionalVariable;
/**
* Constructor
*/
public function __construct()
{
$this->setProperties();
}
/**
* Sets the import plugin properties.
* Called in the constructor.
*
* @return void
*/
protected function setProperties()
{
$importPluginProperties = new PMA\libraries\properties\plugins\ImportPluginProperties();
$importPluginProperties->setText('[name]'); // the name of your plug-in
$importPluginProperties->setExtension('[ext]'); // extension this plug-in can handle
$importPluginProperties->setOptionsText(__('Options'));
// create the root group that will be the options field for
// $importPluginProperties
// this will be shown as "Format specific options"
$importSpecificOptions = new
PMA\libraries\properties\options\groups\OptionsPropertyRootGroup(
"Format Specific Options"
);
// general options main group
$generalOptions = new PMA\libraries\properties\options\groups\OptionsPropertyMainGroup(
"general_opts"
);
// optional :
// create primary items and add them to the group
// type - one of the classes listed in libraries/properties/options/items/
// name - form element name
// text - description in GUI
// size - size of text element
// len - maximal size of input
// values - possible values of the item
$leaf = new PMA\libraries\properties\options\items\RadioPropertyItem(
"structure_or_data"
);
$leaf->setValues(
array(
'structure' => __('structure'),
'data' => __('data'),
'structure_and_data' => __('structure and data')
)
);
$generalOptions->addProperty($leaf);
// add the main group to the root group
$importSpecificOptions->addProperty($generalOptions);
// set the options for the import plugin property item
$importPluginProperties->setOptions($importSpecificOptions);
$this->properties = $importPluginProperties;
}
/**
* Handles the whole import logic
*
* @param array &$sql_data 2-element array with sql data
*
* @return void
*/
public function doImport(&$sql_data = array())
{
// get globals (others are optional)
global $error, $timeout_passed, $finished;
$buffer = '';
while (! ($finished && $i >= $len) && ! $error && ! $timeout_passed) {
$data = PMA_importGetNextChunk();
if ($data === false) {
// subtract data we didn't handle yet and stop processing
$GLOBALS['offset'] -= strlen($buffer);
break;
} elseif ($data === true) {
// Handle rest of buffer
} else {
// Append new data to buffer
$buffer .= $data;
}
// PARSE $buffer here, post sql queries using:
PMA_importRunQuery($sql, $verbose_sql_with_comments, $sql_data);
} // End of import loop
// Commit any possible data in buffers
PMA_importRunQuery('', '', $sql_data);
}
// optional:
/* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */
/**
* Getter description
*
* @return type
*/
private function _getMyOptionalVariable()
{
return $this->_myOptionalVariable;
}
/**
* Setter description
*
* @param type $my_optional_variable description
*
* @return void
*/
private function _setMyOptionalVariable($my_optional_variable)
{
$this->_myOptionalVariable = $my_optional_variable;
}
}
?>

View File

@ -0,0 +1,100 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* This class extends ShapeFile class to cater the following phpMyAdmin
* specific requirements.
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
namespace PMA\libraries\plugins\import;
/**
* 1) To load data from .dbf file only when the dBase extension is available.
* 2) To use PMA_importGetNextChunk() functionality to read data, rather than
* reading directly from a file. Using
* PMA\libraries\plugins\import\ImportShp::readFromBuffer() in place of fread().
* This makes it possible to use compressions.
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
class ShapeFile extends \ShapeFile
{
/**
* Returns whether the 'dbase' extension is loaded
*
* @return boolean whether the 'dbase' extension is loaded
*/
private function _isDbaseLoaded()
{
return extension_loaded('dbase');
}
/**
* Loads ESRI shape data from the imported file
*
* @param string $FileName not used, it's here only to match the method
* signature of the method being overridden
*
* @return void
* @see ShapeFile::loadFromFile()
*/
public function loadFromFile($FileName)
{
$this->_loadHeaders();
$this->_loadRecords();
if ($this->_isDbaseLoaded()) {
$this->_closeDBFFile();
}
}
/**
* Loads metadata from the ESRI shape file header
*
* @return void
* @see ShapeFile::_loadHeaders()
*/
public function _loadHeaders()
{
ImportShp::readFromBuffer(24);
$this->fileLength = loadData("N", ImportShp::readFromBuffer(4));
ImportShp::readFromBuffer(4);
$this->shapeType = loadData("V", ImportShp::readFromBuffer(4));
$this->boundingBox = array();
$this->boundingBox["xmin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->boundingBox["ymin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->boundingBox["xmax"] = loadData("d", ImportShp::readFromBuffer(8));
$this->boundingBox["ymax"] = loadData("d", ImportShp::readFromBuffer(8));
if ($this->_isDbaseLoaded() && $this->_openDBFFile()) {
$this->DBFHeader = $this->_loadDBFHeader();
}
}
/**
* Loads geometry data from the ESRI shape file
*
* @return boolean|void
* @see ShapeFile::_loadRecords()
*/
public function _loadRecords()
{
global $eof;
ImportShp::readFromBuffer(32);
while (true) {
$record = new ShapeRecord(-1);
$record->loadFromFile($this->SHPFile, $this->DBFFile);
if ($record->lastError != "") {
return false;
}
if ($eof) {
break;
}
$this->records[] = $record;
}
}
}

View File

@ -0,0 +1,159 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* This class extends ShapeRecord class to cater the following phpMyAdmin
* specific requirements.
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
namespace PMA\libraries\plugins\import;
/**
* 1) To load data from .dbf file only when the dBase extension is available.
* 2) To use PMA_importGetNextChunk() functionality to read data, rather than
* reading directly from a file. Using
* PMA\libraries\plugins\import\ImportShp::readFromBuffer() in place of fread().
* This makes it possible to use compressions.
*
* @package PhpMyAdmin-Import
* @subpackage ESRI_Shape
*/
class ShapeRecord extends \ShapeRecord
{
/**
* Loads a geometry data record from the file
*
* @param object &$SHPFile .shp file
* @param object &$DBFFile .dbf file
*
* @return void
* @see ShapeRecord::loadFromFile()
*/
public function loadFromFile(&$SHPFile, &$DBFFile)
{
$this->DBFFile = $DBFFile;
$this->_loadHeaders();
switch ($this->shapeType) {
case 0:
$this->_loadNullRecord();
break;
case 1:
$this->_loadPointRecord();
break;
case 3:
$this->_loadPolyLineRecord();
break;
case 5:
$this->_loadPolygonRecord();
break;
case 8:
$this->_loadMultiPointRecord();
break;
default:
$this->setError(
sprintf(
__("Geometry type '%s' is not supported by MySQL."),
$this->shapeType
)
);
break;
}
if (extension_loaded('dbase') && isset($this->DBFFile)) {
$this->_loadDBFData();
}
}
/**
* Loads metadata from the ESRI shape record header
*
* @return void
* @see ShapeRecord::_loadHeaders()
*/
public function _loadHeaders()
{
$this->recordNumber = loadData("N", ImportShp::readFromBuffer(4));
ImportShp::readFromBuffer(4);
$this->shapeType = loadData("V", ImportShp::readFromBuffer(4));
}
/**
* Loads data from a point record
*
* @return array
* @see ShapeRecord::_loadPoint()
*/
public function _loadPoint()
{
$data = array();
$data["x"] = loadData("d", ImportShp::readFromBuffer(8));
$data["y"] = loadData("d", ImportShp::readFromBuffer(8));
return $data;
}
/**
* Loads data from a multipoint record
*
* @return void
* @see ShapeRecord::_loadMultiPointRecord()
*/
public function _loadMultiPointRecord()
{
$this->SHPData = array();
$this->SHPData["xmin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["ymin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["xmax"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["ymax"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["numpoints"] = loadData("V", ImportShp::readFromBuffer(4));
for ($i = 0; $i <= $this->SHPData["numpoints"]; $i++) {
$this->SHPData["points"][] = $this->_loadPoint();
}
}
/**
* Loads data from a polyline record
*
* @return void
* @see ShapeRecord::_loadPolyLineRecord()
*/
public function _loadPolyLineRecord()
{
$this->SHPData = array();
$this->SHPData["xmin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["ymin"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["xmax"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["ymax"] = loadData("d", ImportShp::readFromBuffer(8));
$this->SHPData["numparts"] = loadData("V", ImportShp::readFromBuffer(4));
$this->SHPData["numpoints"] = loadData("V", ImportShp::readFromBuffer(4));
for ($i = 0; $i < $this->SHPData["numparts"]; $i++) {
$this->SHPData["parts"][$i] = loadData(
"V",
ImportShp::readFromBuffer(4)
);
}
$readPoints = 0;
foreach ($this->SHPData["parts"] as &$partData) {
if (!isset($partData["points"])
|| !is_array($partData["points"])
) {
$partData = array(
'points' => array()
);
}
while (!in_array($readPoints, $this->SHPData["parts"])
&& ($readPoints < ($this->SHPData["numpoints"]))
) {
$partData["points"][] = $this->_loadPoint();
$readPoints++;
}
}
}
}

View File

@ -0,0 +1,80 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Provides upload functionalities for the import plugins
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\plugins\import\upload;
use PMA\libraries\plugins\UploadInterface;
/**
* Implementation for the APC extension
*
* @package PhpMyAdmin
*/
class UploadApc implements UploadInterface
{
/**
* Gets the specific upload ID Key
*
* @return string ID Key
*/
public static function getIdKey()
{
return 'APC_UPLOAD_PROGRESS';
}
/**
* Returns upload status.
*
* This is implementation for APC extension.
*
* @param string $id upload id
*
* @return array|null
*/
public static function getUploadStatus($id)
{
global $SESSION_KEY;
if (trim($id) == "") {
return null;
}
if (!array_key_exists($id, $_SESSION[$SESSION_KEY])) {
$_SESSION[$SESSION_KEY][$id] = array(
'id' => $id,
'finished' => false,
'percent' => 0,
'total' => 0,
'complete' => 0,
'plugin' => UploadApc::getIdKey(),
);
}
$ret = $_SESSION[$SESSION_KEY][$id];
if (!PMA_Import_apcCheck() || $ret['finished']) {
return $ret;
}
$status = apc_fetch('upload_' . $id);
if ($status) {
$ret['finished'] = (bool)$status['done'];
$ret['total'] = $status['total'];
$ret['complete'] = $status['current'];
if ($ret['total'] > 0) {
$ret['percent'] = $ret['complete'] / $ret['total'] * 100;
}
if ($ret['percent'] == 100) {
$ret['finished'] = (bool)true;
}
$_SESSION[$SESSION_KEY][$id] = $ret;
}
return $ret;
}
}

View File

@ -0,0 +1,60 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Provides upload functionalities for the import plugins
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\plugins\import\upload;
use PMA\libraries\plugins\UploadInterface;
/**
* Implementation for no plugin
*
* @package PhpMyAdmin
*/
class UploadNoplugin implements UploadInterface
{
/**
* Gets the specific upload ID Key
*
* @return string ID Key
*/
public static function getIdKey()
{
return 'noplugin';
}
/**
* Returns upload status.
*
* This is implementation when no webserver support exists,
* so it returns just zeroes.
*
* @param string $id upload id
*
* @return array|null
*/
public static function getUploadStatus($id)
{
global $SESSION_KEY;
if (trim($id) == "") {
return null;
}
if (!array_key_exists($id, $_SESSION[$SESSION_KEY])) {
$_SESSION[$SESSION_KEY][$id] = array(
'id' => $id,
'finished' => false,
'percent' => 0,
'total' => 0,
'complete' => 0,
'plugin' => UploadNoplugin::getIdKey(),
);
}
$ret = $_SESSION[$SESSION_KEY][$id];
return $ret;
}
}

View File

@ -0,0 +1,91 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Provides upload functionalities for the import plugins
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\plugins\import\upload;
use PMA\libraries\plugins\UploadInterface;
/**
* Implementation for upload progress
*
* @package PhpMyAdmin
*/
class UploadProgress implements UploadInterface
{
/**
* Gets the specific upload ID Key
*
* @return string ID Key
*/
public static function getIdKey()
{
return 'UPLOAD_IDENTIFIER';
}
/**
* Returns upload status.
*
* This is implementation for upload progress
*
* @param string $id upload id
*
* @return array|null
*/
public static function getUploadStatus($id)
{
global $SESSION_KEY;
if (trim($id) == "") {
return null;
}
if (!array_key_exists($id, $_SESSION[$SESSION_KEY])) {
$_SESSION[$SESSION_KEY][$id] = array(
'id' => $id,
'finished' => false,
'percent' => 0,
'total' => 0,
'complete' => 0,
'plugin' => UploadProgress::getIdKey(),
);
}
$ret = $_SESSION[$SESSION_KEY][$id];
if (!PMA_Import_progressCheck() || $ret['finished']) {
return $ret;
}
$status = uploadprogress_get_info($id);
if ($status) {
if ($status['bytes_uploaded'] == $status['bytes_total']) {
$ret['finished'] = true;
} else {
$ret['finished'] = false;
}
$ret['total'] = $status['bytes_total'];
$ret['complete'] = $status['bytes_uploaded'];
if ($ret['total'] > 0) {
$ret['percent'] = $ret['complete'] / $ret['total'] * 100;
}
} else {
$ret = array(
'id' => $id,
'finished' => true,
'percent' => 100,
'total' => $ret['total'],
'complete' => $ret['total'],
'plugin' => UploadProgress::getIdKey(),
);
}
$_SESSION[$SESSION_KEY][$id] = $ret;
return $ret;
}
}

View File

@ -0,0 +1,92 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Provides upload functionalities for the import plugins
*
* @package PhpMyAdmin
*/
namespace PMA\libraries\plugins\import\upload;
use PMA\libraries\plugins\UploadInterface;
/**
* Implementation for session
*
* @package PhpMyAdmin
*/
class UploadSession implements UploadInterface
{
/**
* Gets the specific upload ID Key
*
* @return string ID Key
*/
public static function getIdKey()
{
return ini_get('session.upload_progress.name');
}
/**
* Returns upload status.
*
* This is implementation for session.upload_progress in PHP 5.4+.
*
* @param string $id upload id
*
* @return array|null
*/
public static function getUploadStatus($id)
{
global $SESSION_KEY;
if (trim($id) == '') {
return null;
}
if (!array_key_exists($id, $_SESSION[$SESSION_KEY])) {
$_SESSION[$SESSION_KEY][$id] = array(
'id' => $id,
'finished' => false,
'percent' => 0,
'total' => 0,
'complete' => 0,
'plugin' => UploadSession::getIdKey(),
);
}
$ret = $_SESSION[$SESSION_KEY][$id];
if (!PMA_Import_sessionCheck() || $ret['finished']) {
return $ret;
}
$status = false;
$sessionkey = ini_get('session.upload_progress.prefix') . $id;
if (isset($_SESSION[$sessionkey])) {
$status = $_SESSION[$sessionkey];
}
if ($status) {
$ret['finished'] = $status['done'];
$ret['total'] = $status['content_length'];
$ret['complete'] = $status['bytes_processed'];
if ($ret['total'] > 0) {
$ret['percent'] = $ret['complete'] / $ret['total'] * 100;
}
} else {
$ret = array(
'id' => $id,
'finished' => true,
'percent' => 100,
'total' => $ret['total'],
'complete' => $ret['total'],
'plugin' => UploadSession::getIdKey(),
);
}
$_SESSION[$SESSION_KEY][$id] = $ret;
return $ret;
}
}