我有一个问题,我需要从 RETS 服务器获取数据并将其填充到数据库中,我能够获取除两个表之外的所有表来正确自动化这些数据,但我遇到了最后一个(也是最大的)两个表的问题. 查看后,问题是服务器超时,无法及时完成数据库填充。
我正在使用godaddy服务器并且已经有我可以为程序运行设置的最长时间,我现在唯一能做的就是想办法加速我的代码,以便及时完成,并且想要一些帮助弄清楚这一点。
代码(发布在下面)已修改以满足我的需求:https ://github.com/coreyrowell/retshelper/blob/master/rets_helper.php
虽然它运行良好并且我能够让它工作,但我无法找出加速它的最佳方法。任何帮助将不胜感激。
<?php
/*
.---------------------------------------------------------------------------.
| Software: RETSHELPER - PHP Class to interface RETS with Database |
| Version: 1.0 |
| Contact: corey@coreyrowell.com |
| Info: None |
| Support: corey@coreyrowell.com |
| ------------------------------------------------------------------------- |
| Author: Corey Rowell - corey@coreyrowell.com |
| Copyright (c) 2013, Corey Rowell. All Rights Reserved. |
| ------------------------------------------------------------------------- |
| License: This content is released under the |
| (http://opensource.org/licenses/MIT) MIT License. | |
'---------------------------------------------------------------------------'
*/
/*
.---------------------------------------------------------------------------.
| This software requires the use of the PHPRETS library |
| http://troda.com/projects/phrets/ |
'---------------------------------------------------------------------------'
*/
define("BASE_PATH",dirname(__FILE__)."/");
ini_set('mysql.connect_timeout',0);
ini_set('default_socket_timeout',0);
class RETSHELPER
{
// Defaults
private $rets, $auth, $config, $database, $mysqli, $data, $log, $scriptstart, $scriptend,
$previous_start_time, $current_start_time, $updates_log, $active_ListingRids = array();
public function __construct()
{
// Require PHRETS library
require_once("phrets.php");
// Start rets connection
$this->rets = new phRETS;
$this->scriptstart = date("m-d-y_h-i-s", time());
// RETS Server Info
$this->auth['url'] = 'redacted';//MLS_URL;//MLS_URL;
$this->auth['username'] = 'redacted'; //MLS_USERNAME;
$this->auth['password'] = 'redacted'; //MLS_PASS;
$this->auth['retsversion'] = ''; //USER Agent Version
$this->auth['useragent'] = ''; //USER Agent
// RETS Options
$this->config['property_classes'] = array("A");//,"B","C","D","E","F");
$this->config['KeyField'] = "LIST_1";
$this->config['offset_support'] = TRUE; // Enable if RETS server supports 'offset'
$this->config['useragent_support'] = FALSE;
$this->config['images_path'] = BASE_PATH."listing_photos/";
$this->config['logs_path'] = BASE_PATH."logs/";
$this->config['start_times_path'] = BASE_PATH."logs/";
$this->config['previous_start_time'] = $this->get_previous_start_time();
$this->config['create_tables'] = FALSE; // Create tables for classes (terminates program)
// Log to screen?
$this->config['to_screen'] = TRUE;
// Database Config
$this->database['host'] = 'redacted'; //DB_SERVER;
$this->database['username'] = 'redacted'; //DB_USER;
$this->database['password'] = 'redacted'; //DB_PASS;
$this->database['database'] = 'redacted'; //DB_NAME;
$this->config_init();
// Load the run function
$this->run();
}
private function config_init()
{
// Set offset support based on config
if($this->config['offset_support'])
{
$this->rets->SetParam("offset_support", true);
} else {
$this->rets->SetParam("offset_support", false);
}
if($this->config['useragent_support'])
{
$this->rets->AddHeader("RETS-Version", $this->auth['retsversion']);
$this->rets->AddHeader("User-Agent", $this->auth['useragent']);
}
}
public function run()
{
// Start Logging
$this->logging_start();
// RETS Connection
$this->connect();
// Connect to Database
$this->database_connect();
if($this->config['create_tables'])
{
$this->log_data("Creating database tables, program will exit after finishing.");
foreach ($this->config['property_classes'] as $class)
{
$this->log_data("Creating table for: " . $class);
$this->create_table_for_property_class($class);
}
$this->log_data("Exiting program.");
return;
}
// Get Properties (and images)
$this->get_properties_by_class();
// Close RETS Connection
$this->disconnect();
// Delete inactive listings
$this->database_delete_records();
// Insert new listings
$this->database_insert_records();
// Disconnect from Database
$this->database_disconnect();
// End Logging
$this->logging_end();
// Time for next scheduled update
$this->set_previous_start_time();
}
private function connect()
{
$this->log_data("Connecting to RETS...");
// Connect to RETS
$connect = $this->rets->Connect($this->auth['url'], $this->auth['username'], $this->auth['password']);
if($connect)
{
$this->log_data("Successfully connected to RETS.");
return TRUE;
} else {
$error = $this->rets->Error();
if($error['text'])
{
$error = $error['text'];
} else {
$error = "No error message returned from RETS. Check RETS debug file.";
}
$this->log_error("Failed to connect to RETS.\n".$error);
die();
}
}
private function get_properties_by_class()
{
$this->log_data("Getting Classes...");
foreach ($this->config['property_classes'] as $class)
{
$this->log_data("Getting Class: ".$class);
// Set
$fields_order = array();
$mod_timestamp_field = $this->get_timestamp_field($class);
$previous_start_time = $this->config['previous_start_time'];
$search_config = array('Format' => 'COMPACT-DECODED', 'QueryType' => 'DMQL2', 'Limit'=> 1000, 'Offset' => 1, 'Count' => 1);
/*--------------------------------------------------------------------------------.
| |
| If you're having problems, they probably lie here in the $query and/or $search. |
| |
'--------------------------------------------------------------------------------*/
// Query
$query = "({$mod_timestamp_field}=2016-09-16T00:00:00-2016-09-16T01:00:00)";//{$previous_start_time}+)";
// Run Search
$search = $this->rets->SearchQuery("Property", $class, $query, $search_config);
// Get all active listings
$query_all = "({$mod_timestamp_field}=1980-01-01T00:00:00+)";
$search_all = $this->rets->SearchQuery("Property", $class, $query_all, array('Format'=>'COMPACT', 'Select'=>$this->config['KeyField']));
$tmpArray = array();
while($active_rid = $this->rets->FetchRow($search_all)) {
array_push($tmpArray, $active_rid[$this->config['KeyField']]);
}
$this->active_ListingRids['property_'.strtolower($class)] = $tmpArray;
$data = array();
if ($this->rets->NumRows($search) > 0)
{
// Get columns
$fields_order = $this->rets->SearchGetFields($search);
$this->data['headers'] = $fields_order;
// Process results
while ($record = $this->rets->FetchRow($search))
{
$this_record = array();
// Loop it
foreach ($fields_order as $fo)
{
$this_record[$fo] = $record[$fo];
}
$ListingRid = $record[$this->config['KeyField']];
$data[] = $this_record;
}
}
// Set data
$this->data['classes'][$class] = $data;
$this->log_data("Finished Getting Class: ".$class . "\nTotal found: " .$this->rets->TotalRecordsFound());
// Free RETS Result
$this->rets->FreeResult($search);
}
}
private function get_timestamp_field($class)
{
$class = strtolower($class);
switch($class)
{
case 'a':
$field = "LIST_87";
break;
}
return $field;
}
private function disconnect()
{
$this->log_data("Disconnected from RETS.");
$this->rets->Disconnect();
}
private function database_connect()
{
$this->log_data("Connecting to database...");
$host = $this->database['host'];
$username = $this->database['username'];
$password = $this->database['password'];
$database = $this->database['database'];
// Create connection
$this->mysqli = new mysqli($host, $username, $password, $database);
// Throw error if connection fails
if ($this->mysqli->connect_error) {
$this->log_error("Database Connection Error". $this->mysqli->connect_error);
die('Connect Error (' . $this->mysqli->connect_errno . ') '
. $this->mysqli->connect_error);
}
}
private function database_delete_records()
{
$this->log_data("Updating database...");
// Loop through each table and update
foreach($this->config['property_classes'] as $class)
{
// Get Tables
$table = "rets_property_".strtolower($class);
$activeListings = $this->active_ListingRids['property_'.strtolower($class)];
$sql = "DELETE FROM {$table} WHERE {$this->config['KeyField']} NOT IN (".implode(',', $activeListings).");";
$this->mysqli->query($sql);
if($this->mysqli->affected_rows > 0)
{
$this->log_data("Deleted {$this->mysqli->affected_rows} Listings.");
// return TRUE;
} else if($this->mysqli->affected_rows == 0) {
$this->log_data("Deleted {$this->mysqli->affected_rows} Listings.");
} else {
$this->log_data("Deleting database records failed \n\n" . mysqli_error($this->mysqli));
// return FALSE;
}
}
}
private function database_insert_records()
{
$this->log_data("Inserting records...");
foreach($this->config['property_classes'] as $class)
{
// Get Tables
$table = "rets_property_".strtolower($class);
// Get data
$data_row = $this->data['classes'][$class];
// Defaults
$total_rows = 0;
$total_affected_rows = 0;
// Loop through data
foreach($data_row as $drow)
{
// Clean data
// replace empty with NULL
// and wrap data in quotes
$columns = array();
$values = array();
foreach($drow as $key => $val)
{
if($val === '')
{
$val = '""';
} else {
$val = mysqli_real_escape_string($this->mysqli ,$val);
$val = "'$val'";
}
$columns[] = $key;
$values[] = $val;
}
// Implode data rows with commas
$values = implode(', ', $values);
$columns = implode(', ', $columns);
// Build SQL
$sql = "REPLACE INTO {$table} ({$columns}) VALUES ({$values})";
// Do query
$this->mysqli->query($sql);
if($this->mysqli->affected_rows > 0)
{
$total_affected_rows++;
} else {
$this->log_error("Failed to insert the following record: ".$sql . "\n\n" . mysqli_error($this->mysqli));
}
$total_rows++;
}
$this->log_data("Done inserting data. ".$class."\nTotal Records: ".$total_rows." .\nTotal Inserted: ".$total_affected_rows);
}
}
private function database_disconnect()
{
$this->log_data("Database disconnected...");
// Close connection
$this->mysqli->close();
}
private function create_table_for_property_class($class)
{
// gets resource information. need this for the KeyField
$rets_resource_info = $this->rets->GetMetadataInfo();
$resource = "Property";
// pull field format information for this class
$rets_metadata = $this->rets->GetMetadata($resource, $class);
$table_name = "rets_".strtolower($resource)."_".strtolower($class);
// i.e. rets_property_resi
$sql = $this->create_table_sql_from_metadata($table_name, $rets_metadata, $rets_resource_info[$resource]['KeyField']);
$this->mysqli->query($sql);
}
private function create_table_sql_from_metadata($table_name, $rets_metadata, $key_field, $field_prefix = "")
{
$sql_query = "CREATE TABLE {$table_name} (\n";
foreach ($rets_metadata as $field) {
$field['SystemName'] = "`{$field_prefix}{$field['SystemName']}`";
$cleaned_comment = addslashes($field['LongName']);
$sql_make = "{$field['SystemName']} ";
if ($field['Interpretation'] == "LookupMulti") {
$sql_make .= "TEXT";
}
elseif ($field['Interpretation'] == "Lookup") {
$sql_make .= "VARCHAR(50)";
}
elseif ($field['DataType'] == "Int" || $field['DataType'] == "Small" || $field['DataType'] == "Tiny") {
$sql_make .= "INT({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "Long") {
$sql_make .= "BIGINT({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "DateTime") {
$sql_make .= "DATETIME default '0000-00-00 00:00:00' not null";
}
elseif ($field['DataType'] == "Character" && $field['MaximumLength'] <= 255) {
$sql_make .= "VARCHAR({$field['MaximumLength']})";
}
elseif ($field['DataType'] == "Character" && $field['MaximumLength'] > 255) {
$sql_make .= "TEXT";
}
elseif ($field['DataType'] == "Decimal") {
$pre_point = ($field['MaximumLength'] - $field['Precision']);
$post_point = !empty($field['Precision']) ? $field['Precision'] : 0;
$sql_make .= "DECIMAL({$field['MaximumLength']},{$post_point})";
}
elseif ($field['DataType'] == "Boolean") {
$sql_make .= "CHAR(1)";
}
elseif ($field['DataType'] == "Date") {
$sql_make .= "DATE default '0000-00-00' not null";
}
elseif ($field['DataType'] == "Time") {
$sql_make .= "TIME default '00:00:00' not null";
}
else {
$sql_make .= "VARCHAR(255)";
}
$sql_make .= " COMMENT '{$cleaned_comment}'";
$sql_make .= ",\n";
$sql_query .= $sql_make;
}
$sql_query .= "`Photos` TEXT COMMENT 'Photos Array', ";
$sql_query .= "PRIMARY KEY(`{$field_prefix}{$key_field}`) )";
return $sql_query;
}
private function get_previous_start_time()
{
$filename = "previous_start_time_A.txt";
// See if file exists
if(file_exists($this->config['start_times_path'].$filename))
{
$time=time();
$this->updates_log = fopen($this->config['start_times_path'].$filename, "r+");
$this->previous_start_time = fgets($this->updates_log);
$this->current_start_time = date("Y-m-d", $time) . 'T' . date("H:i:s", $time);
} else {
// Create file
$this->updates_log = fopen($this->config['start_times_path'].$filename, "w+");
fwrite($this->updates_log, "1980-01-01T00:00:00\n");
$this->get_previous_start_time();
}
// fgets reads up to & includes the first newline, strip it
return str_replace("\n", '', $this->previous_start_time);
}
private function set_previous_start_time()
{
$file = $this->config['start_times_path'] . "previous_start_time_A.txt";
$file_data = $this->current_start_time."\n";
$file_data .= file_get_contents($file);
file_put_contents($file, $file_data);
}
private function logging_start()
{
$filename = "Log".date("m-d-y_h-i-s", time()).".txt";
// See if file exists
if(file_exists($this->config['logs_path'].$filename))
{
$this->log = fopen($this->config['logs_path'].$filename, "a");
} else {
// Create file
$this->log = fopen($this->config['logs_path'].$filename, "w+");
}
}
private function log_data($data)
{
$write_data = "\nInfo Message: [".date("m/d/y - h:i:s", time())."]\n------------------------------------------------\n";
$write_data .= $data."\n";
$write_data .= "\n------------------------------------------------\n";
fwrite($this->log, $write_data);
if($this->config['to_screen'])
{
echo str_replace(array("\n"), array('<br />'), $write_data);
}
}
private function log_error($error)
{
$write_data = "\nError Message: [".date("m/d/y - h:i:s", time())."]\n------------------------------------------------\n";
$write_data .= $error."\n";
$write_data .= "\n------------------------------------------------\n";
fwrite($this->log, $write_data);
if($this->config['to_screen'])
{
echo str_replace(array("\n"), array('<br />'), $write_data);
}
}
private function logging_end()
{
$this->scriptend = date("m-d-y_h-i-s", time());
$this->log_data("Closing log file.\n
Start Time: {$this->scriptstart}\n
End Time: {$this->scriptend}");
fclose($this->log);
}
}
// Load the class
$retshelper = new RETSHELPER;
对不起,代码墙;我会缩短它,但我对仍然需要什么和不需要什么感到茫然。再一次,任何帮助或指向正确方向的点将不胜感激。