Skip to content

Instantly share code, notes, and snippets.

@atomicpages
Created October 15, 2014 20:46
Show Gist options
  • Save atomicpages/0ced46ea8a93398671b2 to your computer and use it in GitHub Desktop.
Save atomicpages/0ced46ea8a93398671b2 to your computer and use it in GitHub Desktop.
<?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 = "user@foo.com";
$this->subject = count($this->errors) . " Errors during import";
$this->headers = "From: " . $_SERVER["SERVER_NAME"] . "<noreply@example.com>\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