Запуск MySQL *.sql файлов в PHP
У меня два *.sql
файлы, которые я использую при создании новой базы данных веб-сайта. Первый файл создает все таблицы. Второй файл заполняет некоторые записи по умолчанию. Я хотел бы выполнить эти файлы из PHP. Я также использую Zend_Framework, если это поможет достичь этого.
Дополнительная информация
- У меня нет доступа к консоли
- Я пытаюсь автоматизировать создание сайтов из нашего приложения.
РЕШЕНИЕ
С помощью shell_exec()
...
$command = 'mysql'
. ' --host=' . $vals['db_host']
. ' --user=' . $vals['db_user']
. ' --password=' . $vals['db_pass']
. ' --database=' . $vals['db_name']
. ' --execute="SOURCE ' . $script_path
;
$output1 = shell_exec($command . '/site_db.sql"');
$output2 = shell_exec($command . '/site_structure.sql"');
... Я никогда не получал полезного вывода, но следовал некоторым советам в другом потоке и, наконец, все заработало. Я переключаюсь на --option=value
формат для команд и используется --execute="SOURCE ..."
вместо <
выполнить файл.
Кроме того, я никогда не получал хорошего объяснения разницы между shell_exec()
а также exec()
,
15 ответов
Этот вопрос возникает время от времени. Нет хорошего решения для запуска скрипта.sql напрямую из PHP. Существуют крайние случаи, когда операторы, общие в сценарии.sql, не могут быть выполнены как операторы SQL. Например, инструмент mysql имеет встроенные команды, которые не распознаются MySQL Server, например CONNECT
, TEE
, STATUS
, а также DELIMITER
,
Поэтому я даю +1 на ответ @Ignacio Vazquez-Abrams. Вы должны запустить свой скрипт.sql на PHP, вызвав mysql
инструмент, например с shell_exec()
,
Я получил этот тест работает:
$command = "mysql --user={$vals['db_user']} --password='{$vals['db_pass']}' "
. "-h {$vals['db_host']} -D {$vals['db_name']} < {$script_path}";
$output = shell_exec($command . '/shellexec.sql');
Смотрите также мои ответы на эти связанные вопросы:
$commands = file_get_contents($location);
$this->_connection->multi_query($commands);
Вот что я использую:
function run_sql_file($location){
//load file
$commands = file_get_contents($location);
//delete comments
$lines = explode("\n",$commands);
$commands = '';
foreach($lines as $line){
$line = trim($line);
if( $line && !startsWith($line,'--') ){
$commands .= $line . "\n";
}
}
//convert to array
$commands = explode(";", $commands);
//run commands
$total = $success = 0;
foreach($commands as $command){
if(trim($command)){
$success += (@mysql_query($command)==false ? 0 : 1);
$total += 1;
}
}
//return number of successful queries and total number of queries found
return array(
"success" => $success,
"total" => $total
);
}
// Here's a startsWith function
function startsWith($haystack, $needle){
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
Для этого вам нужно будет создать полный синтаксический анализатор SQL. Я рекомендую вам использовать mysql
вместо этого инструмент командной строки, вызывающий его извне из PHP.
Мне никогда не приходилось его использовать, но у класса mysqli есть метод multi_query:
Я знаю, что опаздываю на вечеринку, но PHP Mini Admin несколько раз спасал меня. По сути, это "облегченный" PHPMyAdmin, содержащийся в одном файле, поэтому нет необходимости в сложных установках, просто загрузите его и войдите в систему.
Не забудьте про phpMyAdmin. Довольно солидный интерфейс для взаимодействия с MySQL.
Я не знаю, решит ли это вашу проблему, так как я не знаю, можете ли вы взаимодействовать с ним напрямую из кода, но просто хотел выбросить его туда.
Вы можете использовать этот скрипт для запуска файлов скриптов MySQL. Вам, конечно, нужно установить $hostName, $userName, $password, $dataBaseName, $port и $fileName.
<?php
function parseScript($script) {
$result = array();
$delimiter = ';';
while(strlen($script) && preg_match('/((DELIMITER)[ ]+([^\n\r])|[' . $delimiter . ']|$)/is', $script, $matches, PREG_OFFSET_CAPTURE)) {
if (count($matches) > 2) {
$delimiter = $matches[3][0];
$script = substr($script, $matches[3][1] + 1);
} else {
if (strlen($statement = trim(substr($script, 0, $matches[0][1])))) {
$result[] = $statement;
}
$script = substr($script, $matches[0][1] + 1);
}
}
return $result;
}
function executeScriptFile($fileName, $dbConnection) {
$script = file_get_contents($scriptFleName);
$statements = parseScript($script);
foreach($statements as $statement) {
mysqli_query($dbConnection, $statement);
}
}
$hostName = '';
$userName = '';
$password = '';
$dataBaseName = '';
$port = '';
$fileName = '';
if ($connection = @mysqli_connect($hostName, $userName, $password, $dataBaseName, $port)) {
executeScriptFile($fileName, $connection);
} else {
die('Can not connect to MySQL');
}
Вот мое решение, и приведенный ниже код объясняет, что делает. Принцип заключается в том, чтобы прочитать файл построчно, построить запрос и выполнить каждый из них. Я видел много решений, использующих "file_get_contents", который не является хорошим решением, потому что он может вызвать проблему с буфером, так как он читает все содержимое файла в строковую переменную. Мое решение также учитывает запросы TRIGGER. Нет выделения массива, комментарии и пустые строки удаляются.
<?php
/**
* Get a connection from database
* @param type $db_host database hostname
* @param type $db_user database username
* @param type $db_password database password
* @param type $db_name database name
* @return \PDO
*/
function get_db_connection($db_host, $db_user, $db_password, $db_name)
{
$dns = "mysql:host=$db_host;dbname=$db_name";
try
{
return new PDO($dns, $db_user, $db_password);
} catch (PDOException $ex)
{
return null;
}
}
/**
* Runs SQL queries from file
*/
function exec_sql_queries_from_file($script_file, $db_host, $db_user, $db_password, $db_name)
{
// to increase the default PHP execution time
set_time_limit ( 60 ); // Max time = 60 seconds
// Connect to database
$connection = get_db_connection($db_host, $db_user, $db_password, $db_name);
// If the connection is acquired
if($connection != null){
// Open sql file
$f = fopen($script_file, 'r');
// sql query
$query = '';
// Default delimiter for queries
$delimiter = ';';
// read line by line
while (!feof($f))
{
$line = str_replace(PHP_EOL, '', fgets($f)); // read a line and remove the end of line character
/* if the current line contains the key word 'DELIMITER'. Ex: DELIMITER ;; or DELIMITER $$
* mostly used for TRIGGERS' queries
*/
if(strpos($line, 'DELIMITER') !== false)
{
// change the delimiter and read the next line
$delimiter = str_replace('DELIMITER ', '', $line);
continue;
}
// Consider the line as part of a query if it's not empty and it's not a comment line
if (!empty($line) && !starts_with($line, '/*') && !starts_with($line, '--'))
{
// the query hasn't reach its end: concatenate $line to $query if $line is not a delimiter
$query .= $line !== $delimiter ? $line : '';
// if the current line ends with $delimiter: end of current query
if (ends_with($line, $delimiter))
{
// exec the query
$connection->exec($query) or die($connection->errorInfo());
// start new query
$query = '';
}
}
}
fclose($f);
}
}
/**
* Starts with function
*/
function starts_with($haystack, $needle)
{
return $haystack{0} === $needle{0} ? stripos($haystack, $needle) === 0 : false;
}
/**
* Ends with function
*/
function ends_with($haystack, $needle)
{
$pos = stripos($haystack, $needle);
return $pos === FALSE ? FALSE : substr($haystack, $pos) === $needle;
}
Я создал скрипт миграции с multi_query
, Он может обрабатывать вывод mysqldump и экспорт phpmyadmin без инструмента командной строки mysql. Я также сделал некоторую логику для обработки нескольких файлов миграции на основе метки времени, хранящейся в БД, таких как Rails. Я знаю, что требуется больше обработки ошибок, но в настоящее время делает работу для меня.
Проверьте это: https://github.com/kepes/php-migration
Я думаю, что если вы не обрабатываете пользовательский ввод с помощью только сценариев, созданных разработчиками, или инструментов экспорта, вы можете использовать его безопасно.
Чтобы выполнить генерацию таблицы из приложения, вы можете создать php-файл, который будет делать это при запуске.
$hostname = "localhost";
$database = "databasename";
$username = "rootuser";
$UserPassword = "password";
$myconnection = mysql_pconnect($hostname, $username , $UserPassword) or trigger_error(mysql_error(),E_USER_ERROR);
mysql_connect($hostname , $username , $UserPassword ) or die(mysql_error());
mysql_select_db($database) or die(mysql_error());
if ( !$myconnection ){ echo "Error connecting to database.\n";}
$userstableDrop = " DROP TABLE IF EXISTS `users`";
$userstableCreate = " CREATE TABLE IF NOT EXISTS `users` (
`UserID` int(11) NOT NULL,
`User_First_Name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=15" ;
$userstableInsert = "INSERT INTO `users` (`UserID`, `User_First_Name`) VALUES
(1, 'Mathew'),
(2, 'Joseph'),
(3, 'James'),
(4, 'Mary')";
$userstableAlter1 = "ALTER TABLE `users` ADD PRIMARY KEY (`UserID`)";
$userstableAlter2 = " ALTER TABLE `users` MODIFY `UserID` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=15";
$createDb_sql = $userstableDrop;
$insertSite = mysql_query($createDb_sql);
$createDb_sql = $userstableCreate;
$insertSite = mysql_query($createDb_sql);
$createDb_sql = $userstableInsert;
$insertSite = mysql_query($createDb_sql);
$createDb_sql = $userstableAlter1;
$insertSite = mysql_query($createDb_sql);
$createDb_sql = $userstableAlter2;
$insertSite = mysql_query($createDb_sql);
echo "Succesful!";
mysql_close($myconnection );
Одно предложение:
// connect to db.
if (mysql_query("SOURCE myfile.sql")) {
echo "Hello Sonny";
}
Я нашел простое решение, это работает для меня
$new_conn=mysqli_connect("localhost","db_user","pass","db_name");
$quries=file_get_contents("db_backup.sql");
$res=mysqli_multi_query($new_conn,$quries);
Просто хочу добавить к ответу @Bill Karwin, приведенному выше.
Вы можете импортировать | повторно инициализировать | выполнить собственный SQL; базу данных с помощью файла сценария sql, просто нажав кнопку. Эта кнопка будет выполнять файл сценария sql с использованием ajax.
например.
Код внешнего интерфейса
<input type="button" value="Execute SQL Script" id="btnExecuteScript" />
<input type="button" value="reset" onclick="clearDiv('divExecuteScript')" />
<div id="divExecuteScript" style='display: none'></div>
<br />
Код jquery, вызывающий ajax
$('#btnExecuteScript').click(function (event) {
if ($('#divExecuteScript').html() == '') {
$('#divExecuteScript').html("<b style='font-family: sans-serif;font-size: larger'>Please Wait, It might take a few minutes</b>");
$('#divExecuteScript').show();
$.get("../controller/Controller.php?executeScript=TRUE", function (data) {
// alert("$" + data + "$");
$('body').css('cursor', 'default');
$('#divExecuteScript').html(data);
$('#divExecuteScript').show();
});
} else
$('#divExecuteScript').toggle();
});
файл подключения
class Conn {
protected $databaseURL; // const
protected $databaseName;
protected $databaseUName;
protected $databasePWord;
public $mysqli;
public function __construct($args = null) {
if (stripos($_SERVER['SERVER_NAME'], "localhost") !== FALSE) {
$this->databaseURL = "host";
$this->databaseName = "database";
$this->databaseUName = "user";
$this->databasePWord = "password";
}
$this->mysqli = new mysqli($this->databaseURL, $this->databaseUName, $this->databasePWord, $this->databaseName) or die('Could not connect to the database server' . mysqli_connect_error());
if (empty($this->mysqli))
die("Error while connecting to host");
}
function get_databaseURL() {
return $this->databaseURL;
}
function get_databaseUName() {
return $this->databaseUName;
}
function get_databasePWord() {
return $this->databasePWord;
}
function get_databaseName() {
return $this->databaseName;
}
}
код контроллера, выполняющий команду
$con = new Conn();
$mysqli = new mysqli($con->get_databaseURL(), $con->get_databaseUName(), $con->get_databasePWord(), $con->get_databaseName()) or die('Could not connect to the database server' . mysqli_connect_error());
if (isset($_GET['executeScript'])) {
$script_path = '/path-to-script-file/filename.sql';
$command = "mysql --user={$con->get_databaseUName()} --password='{$con->get_databasePWord()}' "
. "-h {$con->get_databaseURL()} -D {$con->get_databaseName()} < {$script_path}";
$output = shell_exec($command);
if (!empty($output))
echo "<b style='font-family: sans-serif;font-size: large'>Execute the SQL script<br />";
else
echo "<b style='font-family: sans-serif;font-size: large'>Unable to execute the SQL script</b><br />";
return;
}
PHP-код
Код, который я нашел на этой странице, работал у меня. (Прокрутите вниз, чтобы увидеть версию с комментариями)
<?php
$conn = new mysqli('localhost', 'root', '' , 'sql_auto_test_table');
$query = '';
$sqlScript = file('sqlFileName.sql');
foreach ($sqlScript as $line) {
$startWith = substr(trim($line), 0 ,2);
$endWith = substr(trim($line), -1 ,1);
if (empty($line) || $startWith == '--' || $startWith == '/*' || $startWith == '//') {
continue;
}
$query = $query . $line . "/*<br>*/";
if ($endWith == ';') {
mysqli_query($conn,$query) or die('<div>Problem in executing the SQL query <b>,<br><br>' . $query. '</b><br><br>'.$conn->error.'</div>');
$query= '';
}
}
echo '<div>SQL file imported successfully</div>';
?>
Возможные исправления
Я протестировал этот файл с базой данных WordPress , экспортированной в SQL с помощью phpMyAdmin , и он работал нормально. Мне пришлось добавить следующие строки вверху файла .sql, чтобы избежать нескольких ошибок DEFAULT VALUE в некоторых столбцах DATE. В качестве альтернативы вы можете попробовать выполнить следующие запросы перед выполнением вашего файла SQL, если вы получите аналогичную ошибку.
SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';
SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';
Кроме того, замените насильственную функцию die() на более совершенный механизм обработки ошибок.
Объяснение
Если хотите, я добавил несколько строк комментариев, объясняющих поведение.
<?php
$conn = new mysqli('localhost', 'root', '' , 'db_name');
$query = ''; //Set an empty query variable to hold the query
$sqlScript = file('mySqlFile.sql'); //Set the sql file location
//Read each line of the file
foreach ($sqlScript as $line) {
//Get the starting character and the ending character of each line
$startWith = substr(trim($line), 0 ,2);
$endWith = substr(trim($line), -1 ,1);
//Check for empty or comment lines. (If the line starts with --,/*,// or the line is empty, skip to the next line)
if (empty($line) || $startWith == '--' || $startWith == '/*' || $startWith == '//') {
continue;
}
//Add the line to the query. (Additional optional commented out <br> tag added to query for easy error identification)
$query = $query . $line . "/*<br>*/";
//If the line end with a ";" assume the last query has ended in this line
if ($endWith == ';') {
//Therefore, try to execute the query. Upon failure, display the last formed query with the SQL error message
mysqli_query($conn,$query) or die('<div>Problem in executing the SQL query <b>,<br><br>' . $query. '</b><br><br>'.$conn->error.'</div>');
//Reset the query variable and continue to loop the next lines
$query= '';
}
}
//If nothing went wrong, display a success message after looping through all the lines in the sql file
echo '<div>SQL file imported successfully</div>';
/*
If failed with an invalid DEFAULT value for a DATE column error, try adding the following lines to the top of your SQL file. Otherwise, execute these lines before executing your .sql file.
SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';
SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';
*/
?>