Lynda - 超越基础的PHP - 上传照片时出现数据库错误

时间:2016-01-01 19:41:27

标签: php mysql database oop

我在写这个问题之前进行了搜索,但没想到我会找到答案,因为我的问题是特定的。

无论如何,我一直关注Kevin Skoglund在Lynda.com上的PHP Beyond the Basics课程,并且在将照片上传到数据库(MySQL)时遇到了麻烦。这是我第一次真正涉足OOP并且已经陷入停滞状态。我一直在查看我的文件超过一周,尽力解决问题,没有运气。

奇怪的是,我尝试直接在本地计算机上使用练习文件,并且我得到了相同的错误(包括我的信息,如数据库信用和目录名称)。

基本上我的问题是,当我尝试上传照片时,它会从临时目录移动到images目录,但永远不会进入数据库。我得到了数据库查询失败'它发布时的消息和mysql中的照片表保持为空。我看到错误来自哪里(在database.php confirm_query()内部),但没有关于问题的倾向。我知道我能够与数据库进行通信,因为在创建照片类/表之前,我能够将用户添加到数据库中的用户表。

以下是我的文件。我添加了我认为与此问题相关的三个,但是将整个项目压缩并将其上传到Dropbox。任何帮助/见解都会非常感激!!

*请注意我已将databaseObject中的函数放入照片类

photo_upload.php:

<?php
require_once('../../includes/initialize.php');
if (!$session->is_logged_in()) {
    redirect_to("login.php");
}

$max_file_size = 1048576;   //expressed in bytes
//  10240  = 10kb
// 102400  = 100kb
//1048576  = 1mb
//10485760 = 1mb

   $message = "";
   if (isset($_POST['submit'])) {
    $photo = new Photograph();
    $photo->caption = $_POST['caption'];
    $photo->attach_file($_FILES['file_upload']);
        if ($photo->save()) {
        //success
        $message = "Photograph uploaded successfully";
    } else {
        //failure
        $message = join("<br>", $photo->errors);
    }
}

include_layout_template('admin_header.php');
?>

<h2>Photo Upload</h2>

<?php echo output_message($message); ?>
    <form action="photo_upload.php" enctype="multipart/form-data"   method="post">
    <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $max_file_size ?>">

    <p><input type="file" name="file_upload"></p>

    <p>Caption: <input type="text" name="caption" value=""></p>
    <input type="submit" name="submit" value="upload">
    </form>
<?php include_layout_template('admin_footer.php'); ?>

Photograph.php:

    <?php
// If it's going to need the database, then it's
// probably smart to require it before we start.
require_once(LIB_PATH.DS.'database.php');

class Photograph extends DatabaseObject {

    protected static $table_name="photographs";
    protected static $db_fields=array('id', 'filename', 'type', 'size', 'caption');
    public $id;
    public $filename;
    public $type;
    public $size;
    public $caption;

    private $temp_path;
    protected $upload_dir="images";
    public $errors=array();

    protected $upload_errors = array(
        // http://www.php.net/manual/en/features.file-upload.errors.php
        UPLOAD_ERR_OK               => "No errors.",
        UPLOAD_ERR_INI_SIZE         => "Larger than upload_max_filesize.",
        UPLOAD_ERR_FORM_SIZE        => "Larger than form MAX_FILE_SIZE.",
        UPLOAD_ERR_PARTIAL          => "Partial upload.",
        UPLOAD_ERR_NO_FILE          => "No file.",
        UPLOAD_ERR_NO_TMP_DIR       => "No temporary directory.",
        UPLOAD_ERR_CANT_WRITE       => "Can't write to disk.",
        UPLOAD_ERR_EXTENSION        => "File upload stopped by extension."
);

// Pass in $_FILE(['uploaded_file']) as an argument
public function attach_file($file) {
    // Perform error checking on the form parameters
    if(!$file || empty($file) || !is_array($file)) {
        // error: nothing uploaded or wrong argument usage
        $this->errors[] = "No file was uploaded.";
        return false;
    } elseif($file['error'] != 0) {
        // error: report what PHP says went wrong
        $this->errors[] = $this->upload_errors[$file['error']];
        return false;
    } else {
        // Set object attributes to the form parameters.
        $this->temp_path  = $file['tmp_name'];
        $this->filename   = basename($file['name']);
        $this->type       = $file['type'];
        $this->size       = $file['size'];
        // Don't worry about saving anything to the database yet.
        return true;

    }
}

public function save() {
    // A new record won't have an id yet.
    if(isset($this->id)) {
        // Really just to update the caption
        $this->update();
    } else {
        // Make sure there are no errors

        // Can't save if there are pre-existing errors
        if(!empty($this->errors)) { return false; }

        // Make sure the caption is not too long for the DB
        if(strlen($this->caption) > 255) {
            $this->errors[] = "The caption can only be 255 characters long.";
            return false;
        }

        // Can't save without filename and temp location
        if(empty($this->filename) || empty($this->temp_path)) {
            $this->errors[] = "The file location was not available.";
            return false;
        }

        // Determine the target_path
        $target_path = SITE_ROOT .DS. 'public' .DS. $this->upload_dir .DS. $this->filename;

        // Make sure a file doesn't already exist in the target location
        if(file_exists($target_path)) {
            $this->errors[] = "The file {$this->filename} already exists.";
            return false;
        }

        // Attempt to move the file
        if(move_uploaded_file($this->temp_path, $target_path)) {
            // Success
            // Save a corresponding entry to the database
            if($this->create()) {
                // We are done with temp_path, the file isn't there anymore
                unset($this->temp_path);
                return true;
            }
        } else {
            // File was not moved.
            $this->errors[] = "The file upload failed, possibly due to incorrect permissions on the upload folder.";
            return false;
        }
    }
}


// Common Database Methods
public static function find_all() {
    return self::find_by_sql("SELECT * FROM ".self::$table_name);
}

public static function find_by_id($id=0) {
    $result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE id={$id} LIMIT 1");
    return !empty($result_array) ? array_shift($result_array) : false;
}

public static function find_by_sql($sql="") {
    global $database;
    $result_set = $database->query($sql);
    $object_array = array();
    while ($row = $database->fetch_array($result_set)) {
        $object_array[] = self::instantiate($row);
    }
    return $object_array;
}

private static function instantiate($record) {
    // Could check that $record exists and is an array
    $object = new self;
    // Simple, long-form approach:
    // $object->id              = $record['id'];
    // $object->username    = $record['username'];
    // $object->password    = $record['password'];
    // $object->first_name = $record['first_name'];
    // $object->last_name   = $record['last_name'];

    // More dynamic, short-form approach:
    foreach($record as $attribute=>$value){
        if($object->has_attribute($attribute)) {
            $object->$attribute = $value;
        }
    }
    return $object;
}

private function has_attribute($attribute) {
    // We don't care about the value, we just want to know if the key exists
    // Will return true or false
    return array_key_exists($attribute, $this->attributes());
}

protected function attributes() {
    // return an array of attribute names and their values
    $attributes = array();
    foreach(self::$db_fields as $field) {
        if(property_exists($this, $field)) {
            $attributes[$field] = $this->$field;
        }
    }
    return $attributes;
}

protected function sanitized_attributes() {
    global $database;
    $clean_attributes = array();
    // sanitize the values before submitting
    // Note: does not alter the actual value of each attribute
    foreach($this->attributes() as $key => $value){
        $clean_attributes[$key] = $database->escape_value($value);
    }
    return $clean_attributes;
}

// replaced with a custom save()
// public function save() {
//   // A new record won't have an id yet.
//   return isset($this->id) ? $this->update() : $this->create();
// }

public function create() {
    global $database;
    // Don't forget your SQL syntax and good habits:
    // - INSERT INTO table (key, key) VALUES ('value', 'value')
    // - single-quotes around all values
    // - escape all values to prevent SQL injection
    $attributes = $this->sanitized_attributes();
    $sql = "INSERT INTO ".self::$table_name." (";
    $sql .= join(", ", array_keys($attributes));
    $sql .= ") VALUES ('";
    $sql .= join("', '", array_values($attributes));
    $sql .= "')";
    if($database->query($sql)) {
        $this->id = $database->insert_id();
        return true;
    } else {
        return false;
    }
}

public function update() {
    global $database;
    // Don't forget your SQL syntax and good habits:
    // - UPDATE table SET key='value', key='value' WHERE condition
    // - single-quotes around all values
    // - escape all values to prevent SQL injection
    $attributes = $this->sanitized_attributes();
    $attribute_pairs = array();
    foreach($attributes as $key => $value) {
        $attribute_pairs[] = "{$key}='{$value}'";
    }
    $sql = "UPDATE ".self::$table_name." SET ";
    $sql .= join(", ", $attribute_pairs);
    $sql .= " WHERE id=". $database->escape_value($this->id);
    $database->query($sql);
    return ($database->affected_rows() == 1) ? true : false;
}

public function delete() {
    global $database;
    // Don't forget your SQL syntax and good habits:
    // - DELETE FROM table WHERE condition LIMIT 1
    // - escape all values to prevent SQL injection
    // - use LIMIT 1
    $sql = "DELETE FROM ".self::$table_name;
    $sql .= " WHERE id=". $database->escape_value($this->id);
    $sql .= " LIMIT 1";
    $database->query($sql);
    return ($database->affected_rows() == 1) ? true : false;

    // NB: After deleting, the instance of User still
    // exists, even though the database entry does not.
    // This can be useful, as in:
    //   echo $user->first_name . " was deleted";
    // but, for example, we can't call $user->update()
    // after calling $user->delete().
    }

}

?>

database.php中:

<?php

require_once("config.php");

class MySQLDatabase {

    //Step #1 open connection
    private $connection;

    function __construct() {//once you create an instance of this class it will automatically create the connection
        $this->open_connection();
    }

    public function open_connection(){
        $this->connection = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
        //Test the connection
        if(mysqli_connect_errno()){//use errno because error returns an empty string if succesful
            die('Database connection failed: '. mysqli_connect_error().
                '('.mysqli_connect_errno().')');
        }
    }

    //Step #2 preform database query
    public function query($sql){
        $result = mysqli_query($this->connection, $sql);
        $this->confirm_query($result);
        return $result;
    }

    private function confirm_query($result){
        if(!$result){ //this is a check to make sure the query worked
            die('Database query failed');
        }
    }

    public function escape_value($string){
        $escaped_string = mysqli_real_escape_string($this->connection, $string);
        return $escaped_string;
    }

    //database neutral functions
    //this is our database adapter which is called for mysql
    public function fetch_array($result_set){
        return mysqli_fetch_array($result_set);
    }

    public function num_rows($result_set) {
        return mysqli_num_rows($result_set);
    }

    public function insert_id(){
        //get the last id inserted over the current connection
        return mysqli_insert_id($this->connection);
    }

    public function affected_rows(){
        return mysqli_affected_rows($this->connection);
    }

    //Step #4 close connection
    public function close_connection() {
        if(isset($this->connection)){
            mysqli_close($this->connection);
            unset($this->connection);
        }
    }

}

$database = new MySQLDatabase();



?>

https://www.dropbox.com/s/oqdi2dz2mbkuwzz/photo_gallery.zip?dl=0

1 个答案:

答案 0 :(得分:0)

经过多次挖掘后,我得出的结论是我的问题与我的代码无关。有一个sql设置,不允许我输入并清空字符串sqlmode=STRICT_TRANS_TABLES。谢谢maxhb指出我的方向正确!!

我最终为解决这个问题所做的是创建一个my.conf文件来更改默认设置(我在mac osx 10.11 btw上运行mysql 5.7.9)。奇怪的是,只有当我将这个文件放在两个位置.my.cnf/etc/mysql/my.cnf时,才能使用以下文字:

[mysqld]
sql_mode=NO_ENGINE_SUBSTITUTION