MYSQL file size not showing - php

I'm learning how to build a photo_gallery using a PHP video book/tutorial. I was able to upload a photo, but in the MySQL database, it's not registering a size of the photo. It just says 0 (see image)
The instructor's Mysql gives the size of the file.
Any idea why the file size wouldn't show up in SQL?
UPDATE... This is the photograph class.
<?php
require_once(LIB_PATH.DS.'database.php');
class Photograph extends DatabaseObject {
protected static $table_name="photographs";
protected static $db_fields=array('id', 'filename', 'type', '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(
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 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->error[] = "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
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 {
//failure
$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) {
global $database;
$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){
$object = new self;
//$object->id = $record['id'];
//$object->username = $record['username'];
//$object->password = $record['password'];
//$object->first_name = $record['first_name'];
//$object->last_name = $record['last_name'];
foreach($record as $attribute=>$value) {
if($object->has_attribute($attribute)) {
$object->$attribute = $value;
}
}
return $object;
}
private function has_attribute($attribute) {
$object_vars = $this->attributes();
return array_key_exists($attribute, $object_vars);
}
protected function attributes() {
//return an array of attribute keys 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() {
//return isset($this->id) ? $this->update() : $this->create();
//}
public function create() {
global $database;
$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;
$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;
$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;
}
}
?>

I think you forget to put size in your $db_fields
protected static $db_fields=array('id', 'filename', 'type', 'size', 'caption');
if you look at your attributes() function it checks to see if property_exists within the $db_fields and then assign the value to $attributes array and since 'size' was not within the array that you are checking against, it basically gets filtered out is what i am guessing:
protected function attributes() {
//return an array of attribute keys and their values
$attributes = array();
foreach(self::$db_fields as $field) {
if(property_exists($this, $field)) {
$attributes[$field] = $this->$field;
}
}
return $attributes;
}

Related

Undefined variable when calling a class method

Getting undefined variable error when calling a static method.
I'm new to coding. please be kind.
I'm trying to dynamically display an event page. It has $title, $price, $location, etc. with a table in the database titled Onlineevent.
I wanted to add a photo gallery to this event page and thought it would be better to have a new table (event_gallery) with columns (id, event_id, and image_name). The event_id is a foreign key from the Onlineevent table.
I'm having no problem calling the Onlineevent data from the database with the method (findy_by_id()). However I cannot call the method that relates to the event_gallery. Please refer to the code.
<?php
$event = Onlineevent::find_by_id($_GET['id']);
if($event){
$event_title = $event->event_title;
$event_type = $event->event_type;
$event_location = $event->event_location;
$event_date = $event->event_date;
$event_start_time = $event->event_start_time;
$event_finish_time = $event->event_finish_time;
$max_participants = $event->max_participants;
$event_price = $event->event_price;
$event_description = $event->event_description;
$event_picture = $event->picture_path();
$event_inclusion_1 = $event->inclusion_1;
$event_inclusion_2 = $event->inclusion_2;
$event_inclusion_3 = $event->inclusion_3;
$event_inclusion_4 = $event->inclusion_4;
$event_inclusion_5 = $event->inclusion_5;
$event_inclusion_6 = $event->inclusion_6;
$event_inclusion_7 = $event->inclusion_7;
$event_inclusion_8 = $event->inclusion_8;
}
$images = Eventgallery::find_by_id($_GET['id']);
if($images){
$image = $images->picture_path();
}
echo $image;
?>
Now I'll share the classes.
class Onlineevent extends Db_object{
protected static $db_table = "onlineevent";
protected static $db_table_fields = array('event_type','event_title','event_picture', 'event_location','event_date','event_start_time','event_finish_time','max_participants','event_price','event_description','event_map','inclusion_1','inclusion_2','inclusion_3','inclusion_4','inclusion_5','inclusion_6','inclusion_7','inclusion_8','inclusion_9','inclusion_10');
public $id;
public $event_type;
public $event_title;
public $event_picture;
public $event_location;
public $event_date;
public $event_start_time;
public $event_finish_time;
public $event_koreans;
public $max_participants;
public $event_foreigners;
public $event_price;
public $event_description;
public $event_map;
public $inclusion_1;
public $inclusion_2;
public $inclusion_3;
public $inclusion_4;
public $inclusion_5;
public $inclusion_6;
public $inclusion_7;
public $inclusion_8;
public $inclusion_9;
public $inclusion_10;
public $tmp_path;
public $upload_directory = "images";
public $errors = array();
public $upload_errors_array = array(
0 => 'There is no error, the file uploaded with success',
1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
3 => 'The uploaded file was only partially uploaded',
4 => 'No file was uploaded',
6 => 'Missing a temporary folder',
7 => 'Failed to write file to disk.',
8 => 'A PHP extension stopped the file upload.',
);
This is passing $_FILES['uploaded_file'] as an argument
public function set_file($file) {
if(empty($file) || !$file || !is_array($file)) {
$this->errors[] = "There was no file uploaded here";
return false;
} elseif($file['error'] !=0){
$this->error[] = $this->upload_errors_array[$file['error']];
return false;
} else {
$this->event_picture = basename($file['name']);
$this->tmp_path = $file['tmp_name'];
$this->type = $file['type'];
$this->size = $file['size'];
}
}
public function picture_path(){
return $this->upload_directory.DS.$this->event_picture;
}
public function save(){
if($this->id){
$this->update();
} else {
if(!empty($this->errors)){
return false;
}
if(empty($this->event_picture) || empty($this->tmp_path)){
$this->errors[] = "the file was not available";
return false;
}
$target_path = SITE_ROOT .DS. 'admin'.DS. $this->upload_directory. DS . $this->event_picture;
if(move_uploaded_file($this->tmp_path, $target_path)){
if($this->create()){
unset($this->tmp_path);
return true;
}
} else {
$this->errors[] = "the folder probably does have permission";
return false;
}
}
}
Here is the second class
<?php
class Eventgallery extends Db_object{
protected static $db_table = "event_gallery";
protected static $db_table_fields = array('event_id','image_name');
public $id;
public $event_id;
public $image_name;
public $tmp_path;
public $upload_directory = "images";
public $errors = array();
// public $allowTypes = array('jpg','png','jpeg','gif');
public $upload_errors_array = array(
0 => 'There is no error, the file uploaded with success',
1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
3 => 'The uploaded file was only partially uploaded',
4 => 'No file was uploaded',
6 => 'Missing a temporary folder',
7 => 'Failed to write file to disk.',
8 => 'A PHP extension stopped the file upload.',
);
This is passing $_FILES['uploaded_file'] as an argument
public function set_file($file) {
if(empty($file) || !$file || !is_array($file)) {
$this->errors[] = "There was no file uploaded here";
return false;
} elseif($file['error'] !=0){
$this->error[] = $this->upload_errors_array[$file['error']];
return false;
} else {
$this->image_name = basename($file['name']);
$this->tmp_path = $file['tmp_name'];
$this->type = $file['type'];
$this->size = $file['size'];
}
}
public function picture_path(){
return $this->upload_directory.DS.$this->image_name;
}
public function save(){
if($this->id){
$this->update();
} else {
if(!empty($this->errors)){
return false;
}
if(empty($this->image_name) || empty($this->tmp_path)){
$this->errors[] = "the file was not available";
return false;
}
$target_path = SITE_ROOT .DS. 'admin'.DS. $this->upload_directory. DS . $this->image_name;
if(move_uploaded_file($this->tmp_path, $target_path)){
if($this->create()){
unset($this->tmp_path);
return true;
}
} else {
$this->errors[] = "the folder probably does have permission";
return false;
}
}
}
I would like to be able to call the static function Eventgallery::find_by_id(); so that I can access the data and then print it out on the event page.
Thank you
When the if statement fails, the $image variable is not defined.
if ($images) {
$image = $images->picture_path();
}
echo $image;
You can solve this by declaring it first.
$image = '';
if ($images) {
$image = $images->picture_path();
}
echo $image;

Warning: move_uploaded_file( C: \ xampp \ htdocs\photo_gallery\public\images\powder.jpg): failed to open stream: Invalid argument in C:\xampp\

<?php
// If it's going to need the database, then it's
// probably smart to require it before we start.
require_once('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";
}
}
public function comments() {
return Comment::find_comments_on($this->id);
}
// 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;
}
public static function count_all() {
global $database;
$sql = "SELECT COUNT(*) FROM ".self::$table_name;
$result_set = $database->query($sql);
$row = $database->fetch_array($result_set);
return array_shift($row);
}
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().
}
}
?>
I am getting some warnings on move_upload_file. I tried to diagnose the problem but I failed. Here is the error i am getting.
Warning: move_uploaded_file( C: \ xampp \
htdocs\photo_gallery\public\images\powder.jpg): failed to open stream:
Invalid argument in
C:\xampp\htdocs\photo_gallery\includes\photograph.php on line 88
Warning: move_uploaded_file(): Unable to move
'C:\xampp\tmp\php46A4.tmp' to ' C: \ xampp \
htdocs\photo_gallery\public\images\powder.jpg' in
C:\xampp\htdocs\photo_gallery\includes\photograph.php on line 88
Problem seems to be in SITE_ROOT variable, it should have no spaces on it
As it seems, SITE_ROOT has this value
C: \ xampp \ htdocs\photo_gallery
but it should have this
C:\xampp\htdocs\photo_gallery
If you show us the code where you set SITE_ROOT, we could help you fix it
EDIT (as per your comment on this question)
Your error is in this line you put on the comment of this question
define('SITE_ROOT', " c:\ xampp\htdocs\photo_gallery" );
It should have no spaces, like this
define('SITE_ROOT', "c:\xampp\htdocs\photo_gallery" );

How do I modify an existing file to add the ability to unlink a specific file from a folder?

Thank you StackOverflow experts for looking at my question.
First, It is possible this question has been asked before but my situation is a bit unique. So, please hear me out.
When our users want to edit an existing record, they would also like to have the ability to delete an existing pdf file if one exists before adding a new one.
To display an existing file, I use this code.
<td class="td_input_form">
<?php
// if the BidIDFile is empty,
if(empty($result["BidIDFile"]))
{
//then show file upload field for Bid File
echo '<input type="file" name="BidIDFile[]" size="50">';
}
else
{
// Bid file already upload, show checkbox to delete it.
echo '<input type="checkbox" name="delete[]" value="'.$result["BidIDFile"].'"> (delete)
'.$result["BidIDFile"].'';
}
</td>
Then to delete this file, I use the following code:
// Connect to SQL Server database
include("connections/Connect.php");
// Connect to SQL Server database
include("connections/Connect.php");
$strsID = isset($_GET["Id"]) ? $_GET["Id"] : null;
if(isset($_POST['delete']))
{
// whilelisted table columns
$fileColumnsInTable = array( 'BidIDFile', 'TabSheet', 'SignInSheet', 'XConnect',
'Addend1', 'Addend2','Addend3','Addend4','Addend5', 'Addend6');
$fileColumns = array();
foreach ($_POST['delete'] as $fileColumn)
{
if(in_array($fileColumn, $fileColumnsInTable))
$fileColumns[] = $fileColumn;
}
// get the file paths for each file to be deleted
$stmts = "SELECT " . implode(', ', $fileColumns) . " FROM bids WHERE ID = ? ";
$querys = sqlsrv_query( $conn, $stmts, array($strsID));
$files = sqlsrv_fetch_array($querys,SQLSRV_FETCH_ROW);
// loop over the files returned by the query
foreach ($files as $file )
{
//delete file
unlink($file);
}
// now remove the values from the table
$stmts = "UPDATE bids SET " . impload(' = '', ', $fields) . " WHERE ID = ? ";
$querys = sqlsrv_query( $conn, $stmts, array($strsID));
This works fine. However, the edit file points to an existing file with an INSERT and UPDATE operation in this one file (great thanks to rasclatt) and I am having problem integrating the two together.
Can someone please help with integrating the two files into one?
Thanks in advance for your assistance.
Here is the INSERT and UPDATE file:
<?php
error_reporting(E_ALL);
class ProcessBid
{
public $data;
public $statement;
public $where_vals;
protected $keyname;
protected $conn;
public function __construct($conn = false)
{
$this->conn = $conn;
}
public function SaveData($request = array(),$skip = false,$keyname = 'post')
{
$this->keyname = $keyname;
$this->data[$this->keyname] = $this->FilterRequest($request,$skip);
return $this;
}
public function FilterRequest($request = array(), $skip = false)
{
// See how many post variables are being sent
if(count($request) > 0) {
// Loop through post
foreach($request as $key => $value) {
// Use the skip
if($skip == false || (is_array($skip) && !in_array($key,$skip))) {
// Create insert values
$vals['vals'][] = "'".ms_escape_string($value)."'";
// Create insert columns
$vals['cols'][] = "".str_replace("txt","",$key)."";
// For good measure, create an update string
$vals['update'][] = "".str_replace("txt","",$key)."".' = '."'".ms_escape_string($value)."'";
// For modern day binding, you can use this array
$vals['bind']['cols'][] = "".$key."";
$vals['bind']['cols_bind'][] = ":".$key;
$vals['bind']['vals'][":".$key] = $value;
$vals['bind']['update'][] = "".$key.' = :'.$key;
}
}
}
return (isset($vals))? $vals:false;
}
public function AddFiles($name = 'item')
{
// If the files array has been set
if(isset($_FILES[$name]['name']) && !empty($_FILES[$name]['name'])) {
// Remove empties
$_FILES[$name]['name'] = array_filter($_FILES[$name]['name']);
$_FILES[$name]['type'] = array_filter($_FILES[$name]['type']);
$_FILES[$name]['size'] = array_filter($_FILES[$name]['size']);
$_FILES[$name]['tmp_name'] = array_filter($_FILES[$name]['tmp_name']);
// we need to differentiate our type array names
$use_name = ($name == 'item')? 'Addend':$name;
// To start at Addendum1, create an $a value of 1
$a = 1;
if(!empty($_FILES[$name]['tmp_name'])) {
foreach($_FILES[$name]['name'] as $i => $value ) {
$file_name = ms_escape_string($_FILES[$name]['name'][$i]);
$file_size = $_FILES[$name]['size'][$i];
$file_tmp = $_FILES[$name]['tmp_name'][$i];
$file_type = $_FILES[$name]['type'][$i];
if(move_uploaded_file($_FILES[$name]['tmp_name'][$i], $this->target.$file_name)) {
// Format the key values for addendum
if($name == 'item')
$arr[$use_name.$a] = $file_name;
// Format the key values for others
else
$arr[$use_name] = $file_name;
$sql = $this->FilterRequest($arr);
// Auto increment the $a value
$a++;
}
}
}
}
if(isset($sql) && (isset($i) && $i == (count($_FILES[$name]['tmp_name'])-1)))
$this->data[$name] = $sql;
return $this;
}
public function SaveFolder($target = '../uploads/')
{
$this->target = $target;
// Makes the folder if not already made.
if(!is_dir($this->target))
mkdir($this->target,0755,true);
return $this;
}
public function where($array = array())
{
$this->where_vals = NULL;
if(is_array($array) && !empty($array)) {
foreach($array as $key => $value) {
$this->where_vals[] = $key." = '".ms_escape_string($value)."'";
}
}
return $this;
}
public function UpdateQuery()
{
$this->data = array_filter($this->data);
if(empty($this->data)) {
$this->statement = false;
return $this;
}
if(isset($this->data) && !empty($this->data)) {
foreach($this->data as $name => $arr) {
$update[] = implode(",",$arr['update']);
}
}
$vars = (isset($update) && is_array($update))? implode(",",$update):"";
// Check that both columns and values are set
$this->statement = (isset($update) && !empty($update))? "update bids set ".implode(",",$update):false;
if(isset($this->where_vals) && !empty($this->where_vals)) {
$this->statement .= " where ".implode(" and ",$this->where_vals);
}
return $this;
}
public function SelectQuery($select = "*",$table = 'bids')
{
$stmt = (is_array($select) && !empty($select))? implode(",",$select):$select;
$this->statement = "select ".$stmt." from ".$table;
return $this;
}
public function InsertQuery($table = 'bids')
{
$this->data = array_filter($this->data);
if(empty($this->data)) {
$this->statement = false;
return $this;
}
$this->statement = "insert into ".$table;
if(isset($this->data) && !empty($this->data)) {
foreach($this->data as $name => $arr) {
$insert['cols'][] = implode(",",$arr['cols']);
$insert['vals'][] = implode(",",$arr['vals']);
}
}
$this->statement .= '(';
$this->statement .= (isset($insert['cols']) && is_array($insert['cols']))? implode(",",$insert['cols']):"";
$this->statement .= ") VALUES (";
$this->statement .= (isset($insert['vals']) && is_array($insert['vals']))? implode(",",$insert['vals']):"";
$this->statement .= ")";
return $this;
}
}
include("../Connections/Connect.php");
function render_error($settings = array("title"=>"Failed","body"=>"Sorry, your submission failed. Please go back and fill out all required information."))
{ ?>
<h2><?php echo (isset($settings['title']))? $settings['title']:"Error"; ?></h2>
<p><?php echo (isset($settings['body']))? $settings['body']:"An unknown error occurred."; ?></p>
<?php
}
// this function is used to sanitize code against sql injection attack.
function ms_escape_string($data)
{
if(!isset($data) || empty($data))
return "";
if(is_numeric($data))
return $data;
$non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15
$non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31
$non_displayables[] = '/[\x00-\x08]/'; // 00-08
$non_displayables[] = '/\x0b/'; // 11
$non_displayables[] = '/\x0c/'; // 12
$non_displayables[] = '/[\x0e-\x1f]/'; // 14-31
foreach($non_displayables as $regex)
$data = preg_replace($regex,'',$data);
$data = str_replace("'","''",$data);
return $data;
}
// New bid save engine is required for both sql statement generations
$BidSet = new ProcessBid($conn);
$strId = null;
if(isset($_POST["Id"]))
{
$strId = $_POST["Id"];
//echo $strId;
}
If ($strId == "") {
//echo "This is an insert statement";
// This will generate an insert query
$insert = $BidSet->SaveData($_POST)
->SaveFolder('../uploads/')
->AddFiles('BidIDFile')
->AddFiles('item')
->AddFiles('SignInSheet')
->AddFiles('TabSheet')
->AddFiles('Xcontract')
->InsertQuery()
->statement;
// Check that statement is not empty
if($insert != false) {
sqlsrv_query($conn,$insert);
render_error(array("title"=>"Bid Successfully Saved!","body"=>'Go back to Solicitation screen'));
$err = false;
}
//echo '<pre>';
//print_r($insert);
// echo '</pre>';
}
else
{
//echo "This is an update statement";
// This will generate an update query
$update = $BidSet->SaveData($_POST,array("Id"))
->SaveFolder('../uploads/')
->AddFiles('BidIDFile')
->AddFiles('item')
->AddFiles('SignInSheet')
->AddFiles('TabSheet')
->AddFiles('Xcontract')
->where(array("Id"=>$_POST["Id"]))
->UpdateQuery()
->statement;
//echo '<pre>';
//print_r($update);
//echo '</pre>';
// Check that statement is not empty
if($update != false) {
sqlsrv_query($conn,$update);
render_error(array("title"=>"Bid Successfully Saved!","body"=>'Go back to admin screen'));
$err = false;
}
}
// This will post an error if the query fails
if((isset($err) && $err == true) || !isset($err))
render_error(); ?>

Why is my app running out of memory? Unsetting variables, using chunk

I have the simple app below. I'm turning off query logging in Laravel, I'm unsetting where possible, yet this function will only process about 800 records before I'm out of RAM on my 2GB Linode. I know I'm asking a lot of you guys but I can't seem to see where I'm leaking memory.
There are really only two major steps.
Step 1 - Move records from a temp table to production
class ListingMigrator
{
public function __construct($tempListing, $feed)
{
$this->tempListing = $tempListing;
$this->listing = $this->listingInstance();
$this->feed = $feed;
}
public static function migrateListing($listing, $feed)
{
$instance = new static($listing, $feed);
return $instance->migrate();
}
public function migrate()
{
$this->addExternalData();
$this->populateListing();
$this->processPhotos();
$this->deleteTempListing();
}
private function listingInstance()
{
DB::connection()->disableQueryLog();
$listing = Listing::findByMud($this->tempListing->matrix_unique_id);
return $listing ?: new Listing;
}
private function processPhotos()
{
$retsApi = new RetsFeedApi($this->feed);
/* Initialize Object */
$rets = $retsApi->findMostRecent();
$photos = $rets->getPhotosForListing($this->listing->matrix_unique_id);
foreach ($photos as $photo)
{
$uploader = new PhotoProcessor($this->listing, $photo);
$uploader->process();
}
}
private function populateListing()
{
DB::connection()->disableQueryLog();
$this->listing->fill($this->tempListing->toArray());
$this->listing->imported_at = $this->tempListing->created_at;
$this->listing->board = $this->tempListing->board;
return $this->listing->save();
}
private function addExternalData()
{
// Get Google lattitude and longitude
$googlecoords = getGoogleMapInfo($this->tempListing->FullAddress, $this->tempListing->City);
$this->listing->GoogleLat = $googlecoords['GoogleLat'];
$this->listing->GoogleLong = $googlecoords['GoogleLong'];
// Add or update the Subdivision Table (helper function)
$subdivisiondata = SubdivisionUpdate($this->tempListing->board, $this->tempListing->SubCondoName, $this->tempListing->Development);
$this->listing->SubdivisionID = $subdivisiondata['id'];
}
private function deleteTempListing()
{
return $this->tempListing->delete();
}
}
Step 2 - Download photos and reupload to Amazon S3
class PhotoProcessor
{
public function __construct(Listing $listing, $photoData)
{
$this->bucket = 'real-estate-listings';
$this->s3 = App::make('aws')->get('s3');
$this->tempFileName = 'app/storage/processing/images/retsphotoupload';
$this->photoData = $photoData;
$this->listing = $listing;
$this->photo = new RetsPhoto;
}
public function process()
{
$this->storeTempFile();
$this->storeFileInfo();
$this->buildPhoto();
$success = $this->pushToS3();
// if Result has the full URL or you want to build it, add it to $this->photo
DB::connection()->disableQueryLog();
$this->listing->photos()->save($this->photo);
$this->removeTempFile();
unset ($this->photoData);
return $success;
}
private function storeTempFile()
{
return File::put($this->tempFileName, $this->photoData['Data']) > 0;
}
private function storeFileInfo()
{
$fileInfo = getimagesize($this->tempFileName);
// Could even be its own object
$this->fileInfo = [
'width' => $fileInfo[0],
'height' => $fileInfo[1],
'mimetype' => $fileInfo['mime'],
'extension' => $this->getFileExtension($fileInfo['mime'])
];
}
private function buildPhoto()
{
$this->photo->number = $this->photoData['Object-ID']; // Storing this because it is relevant order wise
$this->photo->width = $this->fileInfo['width'];
$this->photo->height = $this->fileInfo['height'];
$this->photo->path = $this->getFilePath();
}
private function getFilePath()
{
$path = [];
if ($this->listing->City == NULL)
{
$path[] = Str::slug('No City');
}
else
{
$path[] = Str::slug($this->listing->City, $separator = '-');
}
if ($this->listing->Development == NULL)
{
$path[] = Str::slug('No Development');
}
else
{
$path[] = Str::slug($this->listing->Development, $separator = '-');
}
if ($this->listing->Subdivision == NULL)
{
$pathp[] = Str::slug('No Subdivision');
}
else
{
$path[] = Str::slug($this->listing->Subdivision, $separator = '-');
}
if ($this->listing->MLSNumber == NULL)
{
$pathp[] = Str::slug('No MLSNumber');
}
else
{
$path[] = Str::slug($this->listing->MLSNumber, $separator = '-');
}
$path[] = $this->photoData['Object-ID'].'.'.$this->fileInfo['extension'];
return strtolower(join('/', $path));
}
private function pushToS3()
{
return $this->s3->putObject([
'Bucket' => $this->bucket,
'Key' => $this->photo->path,
'ContentType'=> $this->fileInfo['mimetype'],
'SourceFile' => $this->tempFileName
]);
}
private function getFileExtension($mime)
{
// Use better algorithm than this
$ext = str_replace('image/', '', $mime);
return $ext == 'jpeg' ? 'jpg' : $ext;
}
private function removeTempFile()
{
return File::delete($this->tempFileName);
}
}
Edit to show RetsPhoto
class RetsPhoto extends Eloquent {
protected $table = 'rets_property_photos';
public function listing() {
return $this->belongsTo('Listing', 'matrix_unique_id', 'matrix_unique_id');
}
}
Edit #2: Chunk Call
This is in the app/command and the only thing in there is the fire() function below:
public function fire()
{
// Turn off query logging
DB::connection()->disableQueryLog();
$feeds = RetsFeed::where('active','=',1)->get();
foreach ($feeds as $feed)
{
$class = "TempListing{$feed->board}";
$listings = $class::orderBy('MatrixModifiedDT','desc');
$listings->chunk(50, function($listings) use($feed) {
$listings->each(function($listing) use ($feed) {
ListingMigrator::migrateListing($listing,$feed);
echo "Feed: $feed->board\r\n";
echo "SubcondoName: $listing->SubCondoName\r\n";
echo "Development: $listing->Development\r\n";
echo "\r\n";
});
});
}
}
I think I have figured it out.
Your system holds in memory all of the photo data. As witnessed by the unset ($this->photoData);
The problem is that you need to first complete the process function. Your application is not likely processing ANY photos so when you keep grabbing them from the file system you run out of memory BEFORE you even process a single one.
To Confirm this, simply grab 1 file not using the chunk method.
I am not very familar with Laravel, it could be grabbing all of the files all at once as well and eating the ram.
You can do some tracing with memory_get_usage(true) to find out exactly where the ram is getting eaten from. I would suggest analysing the fire method first.

How to create a select list using PHP OOP

I've started recoding a PHP project into OOP. One thing I can't work out among many is how to make a dynamic select list. I have many lookup select lists to make. What's the best way to go about it?
I made a DatabaseObject class which has all my generic database queries in it. Do I add them here or make a special class for them, and how do I go about coding it?
require_once("database.php");
class DatabaseObject {
protected static $table_name;
// find all from a specific table
public static function find_all(){
global $database;
return static::find_by_sql("SELECT * FROM ".static::$table_name);
}
// select all from a specific table
public static function find_all_from($table){
global $database;
return static::find_by_sql("SELECT * FROM " .$table);
}
// find all from a specific table
public static function find_by_id($id){
global $database;
$result_array = static::find_by_sql("
SELECT * FROM ".static::$table_name. " WHERE id = '{$id}' LIMIT 1");
// return the data only for the one user
return !empty($result_array) ? array_shift($result_array) : false;
}
// find using sql
public static function find_by_sql($sql=""){
global $database;
// return all data from sql
$result_set = $database->query($sql);
$object_array = array();
while($row = $database->fetch_array($result_set)){
$object_array[] = static::instantiate($row);
}
return $object_array;
}
protected static function instantiate($record){
$class_name = get_called_class();
$object = new $class_name;
foreach($record as $attribute=>$value){
if($object->has_attribute($attribute)){
$object->$attribute = $value;
}
}
return $object;
}
protected function has_attribute($attribute){
$object_vars = $this->attributes();
// here we only want to know if the key exist
// so we will return true or false
return array_key_exists($attribute, $object_vars);
}
protected function attributes() {
$attributes = array();
foreach(static::$db_fields as $field) {
if(property_exists($this,$field)) {
$attributes[$field]= $this->$field;
}
}
return $attributes;
}
protected function sanitised_attributes() {
global $database;
$clean_attributes = array();
foreach($this->attributes() as $key => $value){
$clean_attributes[$key] = $database->escape_value($value);
}
return $clean_attributes;
}
public function save() {
// A new object won't have an id yet
return isset($this->id) ? $this->update() : $this->create();
}
// create new
protected function create() {
global $database;
$attributes =$this->sanitised_attributes();
$sql = "INSERT INTO ".static::$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;
}
}
// update details
protected function update() {
global $database;
$attributes =$this->sanitised_attributes();
$attribute_pairs = array();
foreach($attributes as $key => $value) {
$attribute_pairs[] = "{$key}='{$value}'";
}
$sql = "UPDATE " .static::$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;
$sql = "DELETE FROM ".static::$table_name;
$sql .= " WHERE id =". $database->escape_value($this->id);
$sql .= " LIMIT 1";
$database->query($sql);
return ($database->affected_rows() ==1) ? true : false ;
}
}
I would definitely model the select list as an object, since it has a well defined responsibility that can be encapsulated. I would go for keeping it as decoupled as possible from the DatabaseObject so that changes in any of those classes don't affect the other. As an example consider:
class SelectList
{
protected $options;
protected $name;
public function __construct($name, $options)
{
$this->options = $options;
$this->name = $name;
}
public function render()
{
$html = "<select name='" . $this->name . "'>\n";
foreach ($this->options as $option)
{
$html .= $option->render();
}
$html .= "</select>\n";
return $html;
}
}
class SelectListOption
{
protected $label;
protected $value;
protected $isSelected;
public function __construct($label, $value, $isSelected = false)
{
//Assign the properties
}
public function render()
{
$html .= '<option value="' . $this->value . '"';
if ($this->isSelected)
{
$html .= ' selected="selected" ';
}
$html .= '>' . $this->label . "</option>\n";
}
}
The one thing I like about modeling things this way is that adding new features (e.g. CSS styles for selected/unselected items, or the disabled attribute) is quite easy, since you know in which object that new feature belongs. Also, having this kind of "small" objects make it quite easy to write unit tests.
HTH
Just create a method which returns an HTML select/options view, by iterating over an associative array passed to the method...? Something like this maybe:
public static function viewSelect($name = "select", $arr_options = array()) {
$html = "<select name='$name'>\n";
foreach ($arr_options as $key => $val) {
$html .= "<option value='$key'>$val</option>\n";
}
$html .= "</select>\n";
return $html;
}
Then just pass the result from one of your database queries to this method. You could put this method into any appropriate class you want to.
You can also add selected option functionality
public static function viewSelect($name = "select", $arr_options =
array(), $selected) {
$selectedhtml = "";
$html = "<select name='$name'>\n";
foreach ($arr_options as $key => $val) {
if($key == $selected) $selectedhtml = "selected";
$html .= "<option value='$key' $selectedhtml>$val</option>\n";
}
$html .= "</select>\n";
return $html; }
public function get_posts()
{
$query="select * from tbl_posts";
$result= mysql_query($query);
$i=0;
while($data= mysql_fetch_assoc($result))
{
foreach($data as $key=>$value)
{
$info[$i][$key]=$value;
}
$i++;
}
return $info;
}

Categories