[英]Php: Learning OOP, Static vs normal method
這是我在Stack Overflow中的第一篇文章,所以如果我不遵守任何規則,請多多包涵。 我已經使用php編寫腳本已有一段時間了,但是從未真正使用過它的OOP方面。 我一直在進行在線培訓課程(不確定是否因為版權而大聲地說出哪一個?)
在編程過程中,無論哪種方式,程序員都在他的一個類中制作了一些靜態方法,我決定我不想將我的方法設為靜態。 這是他在下面編寫的代碼。 我確實對所有相同的東西進行了挖掘,只是對代碼中的變量的引用不同,例如$ this-> variable。
`<?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;
}
}
}
public function destroy() {
// First remove the database entry
if($this->delete()) {
// then remove the file
// Note that even though the database entry is gone, this object
// is still around (which lets us use $this->image_path()).
$target_path = SITE_ROOT.DS.'public'.DS.$this->image_path();
return unlink($target_path) ? true : false;
} else {
// database delete failed
return false;
}
}
public function image_path() {
return $this->upload_dir.DS.$this->filename;
}
public function size_as_text() {
if($this->size < 1024) {
return "{$this->size} bytes";
} elseif($this->size < 1048576) {
$size_kb = round($this->size/1024);
return "{$size_kb} KB";
} else {
$size_mb = round($this->size/1048576, 1);
return "{$size_mb} MB";
}
}
// 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) {
global $database;
$result_array = self::find_by_sql("SELECT * FROM ".self::$table_name." WHERE id=".$database->escape_value($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().
}
}
?>`
^^這是他完整的Photograph()課,下面是我的照片課
<?php
//This class will use the database so wil need the database class included
require_once(LIB_PATH.DS.'database.php');
class Photograph extends DatabaseObject
{
public $table_name = "photographs";
public $id;
public $filename;
public $type;
public $size;
public $caption;
public $all_photos = array();
private $temp_path;
protected $upload_dir = "images";
public $errors=array();
protected $upload_errors = array (
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 Temp Directory.",
UPLOAD_ERR_CANT_WRITE => "Cant write file to disk.",
UPLOAD_ERR_EXTENSION => "File uploaded stopped by extension."
);
//Pass in $_FILE['uploaded_file'] as an argument
public function attach_file($file) {
//Perform error checking on the form params
if(!$file || empty($file) || !is_array($file)) {
//error: nothing uploaded or wrong argument
$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 forms params
$this->temp_path = $file['tmp_name'];
$this->filename = basename($file['name']);
$this->type = $file['type'];
$this->size = $file['size'];
//Dont worry about saving anything to database yet
return true;
}
}
public function save() {
//A new record wont have an id yet
if(isset($this->id)) {
$this->update();
//Really just to update the caption
} else {
//Make sure there are no errors
//Cant save if there are pre existing errors
if(!empty($this->errors)) { return false;}
//Make suer the caption is not to long for the database
if(strlen($this->caption) >= 255) {
$this->errors[] = "The caption can only be 255 characters long.";
return false;
}
//Cant save without the 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 that the file doesn't already exist in that 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()) {
//Were done with the temp path variable, The file isn't there any more
unset($this->temp_path);
return true;
}
} else {
//Error File was not moved
$this->errors[] = "The file upload failed, Possibly due to an incorrect permissions on upload folder.";
return false;
}
}
}
public function destroy($id,$filename) {
//First remove the database entry
if($this->delete($id)) {
//Then remove the file
$target_path = SITE_ROOT.DS.'public'.DS.$this->upload_dir.DS.$filename;
return unlink($target_path) ? true:false;
} else {
//Database delete failed
return false;
}
}
public function image_path() {
return $this->upload_dir.DS;
}
public function size_as_text($file_size) {
if($file_size < 1024) {
return "{$file_size} bytes";
} elseif($file_size < 1048576) {
$size_kb = round($file_size/1024);
return "{$size_kb} KB";
} else {
$size_mb = round($file_size/1048576, 1);
return "{size_mb} MB";
}
}
public function find_all() {
global $database;
$result = $this->find_by_sql("SELECT * FROM ".$this->table_name);
return $result;
}
public function find_all_photos() {
global $database;
$query = "SELECT * FROM {$this->table_name}";
$result = $database->query($query);
return $result;
}
public function find_by_id($id=0) {
global $database;
$result_array = $this->find_by_sql("SELECT * FROM " . $this->table_name . " WHERE id={$database->escape_value($id)} LIMIT 1");
return !empty($result_array) ? $result_array : false;
}
public function find_by_sql($sql="") {
global $database;
$result = $database->query($sql);
$object_array = $database->fetch_array($result);
$object = $this->instantiate($object_array);
//while($row = $database->fetch_array($result)) {
//$object_array[] = $this->instantiate($row);
//}
return $object_array;
}
private function instantiate($record) {
//Could check if $record exists and is an array
//Simple long form approach
//$object = new self;
$this->id= $record['id'];
$this->filename = $record['filename'];
$this->type = $record['type'];
$this->size = $record['size'];
$this->caption = $record['caption'];
//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) {
//get_object_vars returns an assocative array with all attributes
//Incl. pribate ones as the keys and their current values as the value
$object_vars = get_object_vars($this);
//We dont care about the value, we just want to know if the key exists
//Will return true or false
return array_key_exists($attribute,$object_vars);
}
public function create() {
//This is the create method
global $database;
//DOnt 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
$query = "INSERT INTO {$this->table_name} (";
$query .= "filename, type, size, caption";
$query .= ") VALUES ('";
$query .= $database->escape_value($this->filename) . "', '";
$query .= $database->escape_value($this->type) . "', '";
$query .= $database->escape_value($this->size) . "', '";
$query .= $database->escape_value($this->caption) . "')";
if($database->query($query)) {
$this->id = $database->insert_id();
return true;
} else {
return false;
}
}
public function update() {
global $database;
//Dont forget your sql good habits
//UPDATE table SET key='value', key='value' WHERE condition
//single quotes around all values
//Escape all values to prevent sql injection
$query = "UPDATE {$this->table_name} SET ";
$query .= "filename='" . $database->escape_value($this->filename) . "', ";
$query .= "type='" . $database->escape_value($this->type) . "', ";
$query .= "size='" . $database->escape_value($this->size) . "', ";
$query .= "caption='" . $database->escape_value($this->caption) . "'";
$query .= " WHERE id=" . $database->escape_value($this->id);
$database->query($query);
return ($database->affected_rows() ==1) ? true : false;
}
public function delete($id=0) {
global $database;
//Dont forget good sql habits
//DELETE FROM table WHERE condition LIMIT 1
//Escape all values to prevent sql injection
//Use limit 1
$query = "DELETE FROM {$this->table_name} ";
$query .="WHERE id=" . $database->escape_value($id);
$query .= " LIMIT 1";
$database ->query($query);
return ($database->affected_rows() ==1) ? true:false;
}
}
?>
然后,他繼續使用find_all()從view_photos.php頁面上的photos表中獲取所有內容。
$photos = Photograph::find_all();
?>
<?php include_layout_template('admin_header.php'); ?>
<h2>Photographs</h2>
<?php echo output_message($message); ?>
<table class="bordered">
<tr>
<th>Image</th>
<th>Filename</th>
<th>Caption</th>
<th>Size</th>
<th>Type</th>
<th> </th>
</tr>
<?php foreach($photos as $photo): ?>
<tr>
<td><img src="../<?php echo $photo->image_path(); ?>" width="100" /> </td>
<td><?php echo $photo->filename; ?></td>
<td><?php echo $photo->caption; ?></td>
<td><?php echo $photo->size_as_text(); ?></td>
<td><?php echo $photo->type; ?></td>
<td><a href="delete_photo.php?id=<?php echo $photo->id; ? >">Delete</a></td>
</tr>
<?php endforeach; ?>
如您所見,他靜態調用find_all()方法,然后使用該類內部的所有變量,使用存儲在photos變量中的捕獲數據循環遍歷數組以列出數據庫中的所有照片。 下面是我的find_all()方法。
public function find_all() {
global $database;
$result = $this->find_by_sql("SELECT * FROM ".$this->table_name);
return $result;
}
當我做同樣的事情但帶有對象Photograph的實例時
$photo_object = new Photograph();
$photos = find_all();
如果我嘗試然后執行與他相同的循環,我一次又一次地得到相同的結果(數據庫中當前有4個條目,而我得到的完全相同的條目大約是10次)
這是我的代碼,導致該輸出
<?php require_once("../../includes/initialize.php"); ?>
<?php if(!$session->is_logged_in()) { redirect_to("login.php");} ?>
<?php
//This is the class being called
$photo_object = new Photograph();
//Find all photos will return the result from the database. We then need to call the fetch array onit
//$photos = $photo_object->find_all_photos();
$photos = $photo_object->find_all();
?>
<?php include_layout_template('admin_header.php'); ?>
<h2>Photographs: List</h2>
<?php echo output_message($message);?>
<table class="bordered">
<tr>
<th>Image</th>
<th>Filename</th>
<th>Caption</th>
<th>Size</th>
<th>Type</th>
<th> </th>
</tr>
<?php foreach($photos as $photo): ?>
<tr>
<td><img src="<?php echo "../" . $photo_object->image_path() . $photo_object->filename;?>" width="100" /></td>
<td><?php echo $photo_object->filename; ?></td>
<td><?php echo $photo_object->caption; ?></td>
<td><?php echo $photo_object->size_as_text($photo_object->size); ?></td>
<td><?php echo $photo_object->type ?></td>
<td><a href="delete_photo.php?id=<?php echo $photo_object->id;?>">Delete</a> </td>
</tr>
<?php endforeach; ?>
</table>
<br />
<a href="photo_upload.php">Upload a new photograph</a>
<?php include_layout_template('admin_footer.php'); ?>
上面的代碼輸出
`圖片庫:管理員
<tr>
<th>Image</th>
<th>Filename</th>
<th>Caption</th>
<th>Size</th>
<th>Type</th>
<th> </th>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
<tr>
<td><img src="../images\bamboo.jpg" width="100" /></td>
<td>bamboo.jpg</td>
<td>This is a photo of some bamboo</td>
<td>445 KB</td>
<td>image/jpeg</td>
<td><a href="delete_photo.php?id=1">Delete</a></td>
</tr>
</table>
<br />
<a href="photo_upload.php">Upload a new photograph</a>
</div>
<div id="footer">Copyright 2015, Ryan Hadley</div>
</body>
</html>
`
此方法必須是靜態的嗎? 還是我只是沒看到什么,我通過編寫新方法解決了
public function find_all_photos() {
global $database;
$query = "SELECT * FROM {$this->table_name}";
$result = $database->query($query);
return $result;
}
這有效,但是我不使用類內部的任何變量,而是調用了在我的foreach循環中創建的照片數組(foreach($ photos as $ photo))。 這使得類內部的屬性顯得毫無意義。 我這樣做正確嗎? 還是我只是不了解對象?
預先感謝您為我提供的任何幫助
快樂編碼
您現在擁有的代碼將$object_array
傳遞給instantiate
。
但是instantiate
被構建為僅接受從結果集中檢索的row
。
如果在傳遞$object_array
之前對其進行檢查,您將了解為什么會得到不想要的結果。
public function find_by_sql($sql="") {
global $database;
$result = $database->query($sql);
$object_array = $database->fetch_array($result);
// Examine $object_array:
print '<pre>';
var_dump($object_array);
print '</pre>';
$object = $this->instantiate($object_array);
return $object_array;
}
關於以下注釋行:
//while($row = $database->fetch_array($result)) {
//$object_array[] = $this->instantiate($row);
//}
我建議取消注釋,並檢查$row
:
while( $row = $database->fetch_array($result) ) {
// Examine $row:
print '<pre>';
print_r( $row );
print '</pre>';
//$object_array[] = $this->instantiate($row);
}
關於函數instantiate
-您現在使用它的方式,每次調用它時,都會設置其自身的屬性(id,filename等) 。 因此,最后一次調用此函數時,Photograph實例的屬性值與傳遞給它的最后一個$record
的值相同。
private function instantiate($record) {
//Could check if $record exists and is an array
//Simple long form approach
//$object = new self;
$this->id= $record['id'];
$this->filename = $record['filename'];
$this->type = $record['type'];
$this->size = $record['size'];
$this->caption = $record['caption'];
}
這是一個開始,可讓您了解為何看到的結果是相同結果的10倍。
我寫了一個例子來說明您將如何做(根據您的評論確定):
/**
* A new class 'Photographs' to 'manage' the collection of photographs:
**/
class Photographs {
protected static $table_name="photographs";
public $photographObjects = array();
function __construct() {
// The constructor runs on creation/instantiation of the 'Photographs' object
print 'Photographs object created...';
}
// Retrieves the data and instantiates Photograph objects:
public function get_photographs() {
global $database;
$sql = "SELECT * FROM ". $this->table_name;
$this->photographObjects = array(); // empty the array before filling it
$result = $database->query($sql);
while( $row = $database->fetch_array( $result ) ) {
$tmpPhotograph = new Photograph();
$tmpPhotograph->id = $row['id'];
$tmpPhotograph->filename = $row['filename'];
// etc etc (or make the 'instantiate' function of the 'Photograph' class public, so you can do:
// $tmpPhotograph->instantiate($row);
// Now add a created and initialized object to your array:
$this->photographObjects[] = $tmpPhotograph;
}
}
// render the photographs:
public function render_photographs() {
if ( count($this->photographObjects) > 0 ) {
// loop through the objects in the photograpsObjects array:
foreach ( $this->photographObjects as $tmpPhotograph ) {
// in this line you can see that public properties (filename), and functions (image_path()) are available:
print '<img src="../' . $tmpPhotograph->image_path() . $tmpPhotograph->filename . '" width="100" />';
}
} else {
// Nothing found, inform the user:
print 'No photographs in array';
}
}
}
使用方法:
$photographManager = new Photographs();
$photographManager->get_photographs(); // retrieve them
$photographManager->render_photographs(); // output them
或您想使用線路時:
<?php foreach($photos as $photo): ?>
您可以這樣做:
<?php foreach( $photographManager->photographObjects as $photo ): ?>
我希望這可以為您解決一些問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.