Created
October 15, 2014 20:46
-
-
Save atomicpages/0ced46ea8a93398671b2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class CouldNotConnectDatabaseException | |
* @author Dennis Thompson | |
* @license MIT | |
* @version 1.0 | |
*/ | |
class CouldNotConnectDatabaseException extends Exception { | |
public function __construct($message, $code = 0, Exception $previous = null) { | |
parent::__construct($message, $code, $previous); | |
} | |
} | |
/** | |
* Class FileNotFoundException | |
* @author Dennis Thompson | |
* @license MIT | |
* @version 1.0 | |
*/ | |
class FileNotFoundException extends Exception { | |
public function __construct($message, $code = 0, Exception $previous = null) { | |
parent::__construct($message, $code, $previous); | |
} | |
} | |
/** | |
* A really simple logging class. | |
* @author Dennis Thompsoin | |
* @license MIT | |
*/ | |
class Log { | |
private $handle; | |
public function __construct($file, $mode = "a") { | |
$this->handle = fopen($file, $mode); | |
} | |
/** | |
* Writes info to the log | |
* @param mixed $entries and string or an array to write to log | |
* @access public | |
*/ | |
public function log($entries) { | |
if(is_string($entries)) { | |
fwrite($this->handle, "Error: [" . date("d/M/Y H:i:s") . "] " . $entries . "\n"); | |
} else { | |
foreach($entries as $value) { | |
fwrite($this->handle, "Error: [" . date("d/M/Y H:i:s") . "] " . $value . "\n"); | |
} | |
} | |
} | |
} | |
/** | |
* A simple database class that uses an active record approach to writing information to the database | |
* @author Dennis Thompson | |
* @license MIT | |
* @since 1.0 | |
*/ | |
class Database { | |
private $db, $log; | |
public function __construct($host, $user, $pass, $db, $log) { | |
try { | |
if(!is_object($log)) { | |
throw new InvalidArgumentException('$log expected to be an object. Got ' . gettype($log), 2); | |
} | |
$this->log = &$log; | |
} catch(InvalidArgumentException $e) { | |
$this->log = new Log(Import::LOG_NAME); // create a new instance | |
$this->log->log($e->message); | |
} | |
$this->db = new mysqli($host, $user, $pass, $db); | |
} | |
/** | |
* Inserts information into the database using an active record approach and prepared statements | |
* @param string $table the name of the table to write | |
* @param array $data the array of data to write to the table | |
* @access public | |
* @throws InvalidArgumentException | |
*/ | |
public function insert($table, $data) { | |
if(!is_array($data)) { | |
$msg = '$data expects to be an array or an object. Got ' . gettype($data); | |
$this->log->log($msg); | |
throw new InvalidArgumentException($msg, 1); | |
} | |
$handle = $this->db->prepare("INSERT INTO `" . $table . "` (room, mon, tue, wed, thu, fri, sat, sun) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); // prepared statements are a safer way to send data to a database | |
$handle->bind_param('ssssssss', $room, $mon, $tue, $wed, $thu, $fri, $sat, $sun); | |
list($room, $mon, $tue, $wed, $thu, $fri, $sat, $sun) = $data; | |
$handle->execute(); | |
} | |
/** | |
* Inserts information into the database using active record approach | |
* @param string $table | |
* @param array $data a 2D array | |
* @access public | |
* @uses Database::insert() | |
* @throws InvalidArgumentException | |
*/ | |
public function insert_batch($table, $data) { | |
if(!is_array($data)) { | |
$msg = '$data expects to be an array or an object. Got ' . gettype($data); | |
$this->log->log($msg); | |
throw new InvalidArgumentException($msg, 1); | |
} | |
foreach($data as $value) { | |
$this->insert($table, $value); | |
} | |
} | |
} | |
class Import { | |
const LOG_NAME = "csv_import_error.log"; | |
const REMOTE_URL = "http://example.com/"; | |
const REMOTE_FILE = "file.csv"; | |
const LOCAL_CSV_FILENAME = "local_csv.csv"; | |
private $errors, $mail_on_failure, $to, $subject, $headers, $db, $log; | |
public function __construct($mail_on_failure = true) { | |
$this->log = new Log(self::LOG_NAME); | |
$this->db = new Database("127.0.0.1", "root", "", "my_database", $this->log); // create a new database object | |
$this->errors = array(); // initialize errors to an empty array | |
$this->mail_on_failure = $mail_on_failure; | |
if($this->mail_on_failure) { | |
$this->to = "[email protected]"; | |
$this->subject = count($this->errors) . " Errors during import"; | |
$this->headers = "From: " . $_SERVER["SERVER_NAME"] . "<[email protected]>\r\n"; | |
$this->headers .= "X-Mailer: PHP/" . phpversion(); | |
} | |
} | |
/** | |
* Change who the failure email is sent to | |
* @access public | |
* @since 1.0 | |
*/ | |
public function to($to) { | |
$this->to = $to; | |
} | |
/** | |
* Change the subject line of the failure email | |
* @access public | |
* @since 1.0 | |
*/ | |
public function subject($subject) { | |
$this->subject = $subject; | |
} | |
/** | |
* Change the headers of the failure email | |
* @access public | |
* @since 1.0 | |
*/ | |
public function headers($headers) { | |
$this->headers = $headers; | |
} | |
/** | |
* Gets the errors for custom error handling | |
* @return array | |
* @access public | |
* @since 1.0 | |
*/ | |
public function getErrors() { | |
return $this->errors; | |
} | |
/** | |
* Parses the CSV file into an array and imports the file into the database | |
* @param string [ $file = self::LOCAL_CSV_FILENAME ] | |
* @param string [ $delimiter = "," ] | |
* @param string [ $enclosure = "'" ] | |
* @param string [ $escape = "\\" ] | |
* @access public | |
* @uses Database | |
* @throws FileNotFoundException | |
*/ | |
public function import($file = self::LOCAL_CSV_FILENAME, $delimiter = ",", $enclosure = "'", $escape = "\\") { | |
if(!file_exists($file)) { | |
$this->log->log($this->errors[] = "'" . $file . "' does not exist or the path is invalid"); | |
$this->sendErrorEmail(); // force email before exception is thrown | |
throw new FileNotFoundException($this->errors[count($this->errors) - 1]); | |
} | |
$handle = fopen($file, "r"); | |
$data = fgetcsv($handle, 0, $delimiter, $enclosure, $escape); | |
$this->db->insert_batch("times", array_chunk($data, 8)); | |
} | |
/** | |
* Gets a file from a remote server and downloads it | |
* @access public | |
*/ | |
public function getRemoteFile() { | |
if(function_exists("curl_version")) { | |
$file = $this->getRemoteFileCurl(); | |
$handle = fopen(self::LOCAL_CSV_FILENAME, "w"); | |
fwrite($handle, $file); | |
fclose($handle); | |
} else { | |
$this->getRemoteFileCopy(); | |
} | |
} | |
/** | |
* Sends email upon failure to import CSV | |
* @access private | |
*/ | |
private function sendErrorEmail() { | |
$message = "Errors:\r\n"; | |
for($i = 0; $i < count($this->errors); $i++) { | |
$message .= "#" . $i . " " . $this->errors[$i] . "\r\n"; | |
} | |
if(mail($this->to, $this->subject, $message, $this->headers)) { | |
$this->log->log("Success sending error report to " . $this->to); | |
} else { | |
$this->log->log($this->errors[] = "Error sending failure email. Issues are described below"); | |
} | |
} | |
/** | |
* Gets remote file and returns it's contents using cURL | |
* @access private | |
* @return string | |
*/ | |
private function getRemoteFileCurl() { | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, self::REMOTE_URL . self::REMOTE_FILE); | |
curl_setopt($ch, CURLOPT_VERBOSE, 0); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_AUTOREFERER, false); | |
curl_setopt($ch, CURLOPT_REFERER, self::REMOTE_URL); | |
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | |
curl_setopt($ch, CURLOPT_HEADER, 0); | |
$result = curl_exec($ch); | |
curl_close($ch); | |
return $result; | |
} | |
/** | |
* Gets remote file and returns it's contents using copy | |
* @throws Exception | |
*/ | |
private function getRemoteFileCopy() { | |
if(!copy(self::REMOTE_URL . self::REMOTE_FILE, "./" . self::LOCAL_CSV_FILENAME)) { | |
$msg = "Failed to copy " . self::REMOTE_URL . self::REMOTE_FILE . " into " . self::LOCAL_CSV_FILENAME; | |
$this->log->log($msg); | |
$this->sendErrorEmail(); | |
throw new Exception($msg, 1); | |
} | |
} | |
} | |
$foo = new Import(); | |
$foo->getRemoteFile(); | |
$foo->import(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment