One step closer to world domination
PHP
Reduce Drupal system load caused by 404s
Jul 21st
Running multiple large Drupal sites on one machine has created some performance anxiety for us. Certain high traffic periods combined with aggressive web crawlers have ground our servers to a halt on a few occasions.
My latest investigation went into cleaning up popular 404 requests on our larger sites. This noticeably reduced disk I/O. I wanted to take 404 a step further and change the way Drupal 404s were handled on our machines. I turned to the Fast 404 module.
Fast 404 works by intercepting the Drupal bootstrap process and performing its own path and file checking. Fast 404 configuration and execution is performed in the site’s settings.php file.
Version 6.x-1.1 did not work immediately out of the box and required a few changes and bug fixes. I have submitted them to the module author.
The whitelisting did not seem to work so I altered line 38 from:
if (in_array($_SERVER['QUERY_STRING'],$allowed)) { return TRUE; // URL is whitelisted. Assumed good. }
To:
foreach ($allowed as $needle) { if (stristr($_SERVER['QUERY_STRING'],$needle)) { return TRUE; // URL is whitelisted. Assumed good. } }
Line 111:
$sql = "SELECT path FROM menu_router WHERE '%s' LIKE CONCAT(path,'%')"; $sql2 = "SELECT pid FROM url_alias WHERE '%s' LIKE CONCAT(dst,'%')"; $sql3 = "SELECT rid FROM path_redirect WHERE '%s' LIKE source";
Change to:
$sql = "SELECT path FROM " . $db_prefix['default'] . "menu_router WHERE path LIKE '%%s%'"; $sql2 = "SELECT pid FROM " . $db_prefix['default'] . "url_alias WHERE dst LIKE '%%s%' "; $sql3 = "SELECT rid FROM " . $db_prefix['default'] . "path_redirect WHERE '%s' LIKE source";
I also changed line 48 to remove duplicate code:
header('HTTP/1.0 404 Not Found'); $fast_404_html = variable_get('fast_404_html','<html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'); print strtr($fast_404_html, array('@path' => check_plain(request_uri()))); exit();
Change to the function that calls the same code:
fast_404_error_return();
One limitation of the Fast 404 module are that it will print a generic “Page Not Found” message on a white page. We would prefer to have a themed landing page similar to Drupal’s normal 404 page. My solution was to create a static 404.html file for the site and tell Fast 404 to print the contents of the file instead of its generic message.
$conf['fast_404_html'] = file_get_contents('./sites/pathtosite/404.html');
I also wanted to keep track of the requests Fast 404 was catching. I added a direct syslog() call. If your Drupal site is not currently configured to use Syslog see my post on setup and configuration. The log format I chose fit our needs, you could format it however you like.
Around line 186, inside fast_404_error_return():
syslog(1, $_SERVER['SERVER_NAME'].' Fast_404 '.$_SERVER['REMOTE_ADDR'].' '.$_SERVER['SCRIPT_URI'].' '.$_SERVER['HTTP_USER_AGENT'].' '.$_SERVER['HTTP_REFERER']);
I think monitored my Drupal log file for false positives and added them to the white list.
Take a look at your Drupal site and see if you have a lot of 404s being served. If so, try this module out and see if load is reduced.
Setup Syslog on Drupal 6.x and 7.x
Feb 24th
By design Drupal 6.x sites will log all activity to the database using a core feature called watchdog. Not having to require users to configure permissions for a log file on the server reduces install instructions (and possible errors) and creates a more seamless experience for Drupal users and administrators. Unfortunately, this makes monitoring a large multisite installs more laborious.
Enter Syslog.
Syslog is a local and network logging service that has been around since the 1980s. Most *nix systems come preloaded with Syslog and fortunately for us Drupal has a Syslog module included with the 6.x and 7.x cores. Here’s how to set it up:
1) Install & configure the Syslog module
Go to your module install page, /admin/build/modules and install the Syslog module. Then go to the Syslog settings page, /admin/settings/logging/syslog, and select which Syslog level to attach to the log messages. Choose one that is not in use by Syslog
2) Configure Syslog
Edit /etc/syslog.conf and add:
local0.* /var/log/drupal.log
Then, restart Syslog
3) Disable Watchdog database logging
This will require us to edit the Drupal core (oh noes)
Edit drupal/modules/dblog/dblog.module and comment out the contents of dblog_watchdog() (Around line 132)
Original:
function dblog_watchdog($log = array()) { $current_db = db_set_active(); db_query("INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)", $log['user']->uid, $log['type'], $log['message'], serialize($log['variables']), $log['severity'], $log['link'], $log['request_uri'], $log['referer'], $log['ip'], $log['timestamp']); if ($current_db) { db_set_active($current_db); } }
Change to:
function dblog_watchdog($log = array()) { /* $current_db = db_set_active(); db_query("INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)", $log['user']->uid, $log['type'], $log['message'], serialize($log['variables']), $log['severity'], $log['link'], $log['request_uri'], $log['referer'], $log['ip'], $log['timestamp']); if ($current_db) { db_set_active($current_db); } */ }
And you’re done.
Use Drush to update a module in a Drupal multisite environment
Jan 10th
I love Drush. It makes managing my Drupal multisite installs much less complicated. I used to write custom scripts to loop through all the sites an perform actions but this little bit of code has made my maintenance tasks so much simpler.
for SITE in `ls /path/to/drupal/sites | grep "\."`; do /path/to/drush --uri=$SITE --root=/path/to/drupal --yes updatedb done
Programmatically create users in Drupal 6.x
Dec 16th
You can create a Drupal user by sending user_save() a null parameter and an array of user information. You can also assign a role by setting an array that is keyed with the role ID (rid). This can be obtained by calling user_roles() as shown in the example below.
// Get an array of roles $roles = user_roles(); // Basic account information $user = array( 'name' => 'Some User', 'pass' => 'some_password', 'mail' => 'user@example.com', 'status' => 1, 'init' => 'user@example.com', 'roles' => array(array_search('some_role', $roles) => 1), ); // See if the user exists by calling Drupal's user_load() $existing_user = user_load(array('name' => $user['name'])); if (!$existing_user->uid) { // Save the user $user = user_save(NULL, $user); }
Simple Reg-Ex to Validate an Email Address
Nov 24th
Regular expressions can be a pain sometimes. Here’s a code snippet to make your life a little easier when validating email addresses:
$email = 'user@example.com'; if (!eregi("^[_\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\.)+[a-z]{2,3}$", $email)) { print_error("Your email address is invalid!"); }
PHP MySQL class example
Feb 27th
Here is an example of a PHP 4.2 MySQL connector class. It handles all basic database queries and input filtering and validation.
<?php /**************************************************** * * File: class.mysql.php * Author: Matt West, http://mattdanger.net * Date: February 22, 2007 * Purpose: MySQL Class * ****************************************************/ class MySQL_Connection { // Private variables var $handler; var $hostname; var $username; var $password; var $database; // Public variables var $result; var $num_rows; /** * Constructor: Construct the object. * @param string $hostname Hostname of MySQL server (Default: localhost) * @param string $username Your MySQL account username * @param string $password Your MySQL account password * @param string $database The MySQL database to connect to */ function MySQL_Connection($hostname, $username, $password, $database) { $this->hostname = $hostname; $this->username = $username; $this->password = $password; $this->database = $database; $this->connect(); $this->select_db(); } /** * Destructor: Clean up */ function __destruct() { $this->db_close(); } /** * throw_error(): Print an error message * @param string $message A descriptive error message that will print when invoked */ function throw_error($message, $line = 0){ $line = ( !empty($line) ) ? $line = ' on line ' . $line : '' ; die ('There was an error on line' . $line .' in class "' . __CLASS__ . '": ' . $message); } /** * connect(): Connect to MySQL */ function connect() { $this->handler = mysql_pconnect($this->hostname, $this->username, $this->password) or $this->throw_error(mysql_error(), __LINE__); } /** * select_db(): Select the MySQL database */ function select_db() { mysql_select_db($this->database, $this->handler) or $this->throw_error(mysql_error(), __LINE__); } /** * select(): Select a row or rows from the DB * @param string $table Table name * @param array $columns Either '*' or a list of columns to select * @param array $where column => value * @param array $order_by column1 => DESC, column2 => ASC * @param array $limit 1 or 30 or 0, 30 or 30, 60 (etc) * @param bool $debug Returns query string if true * @returns MySQL resource */ function select($table, $columns, $where = '', $order_by = '', $limit = '', $debug = 0) { if ( $table == '' || !sizeof($columns)) { return; } // Set up columns $tmp = ''; if ( $columns != '*' ) { if ( is_array($columns) ) { foreach($columns as $column) { $data .= '`' . $column . '`, '; } $columns = preg_replace( "/, $/" , "" , $data); } else { $columns = '`' . $columns . '`'; } } // Set up the WHERE if ( !empty($where) ) { $tmp = ''; foreach ($where as $key => $val) { $tmp .= '`' . $key . "` = '" . $this->escape_str($val) . "' AND "; } $where = ' WHERE ' . preg_replace( "/ AND $/" , "" , $tmp); } // Set up the ORDER BY $tmp = ''; if ( !empty($order_by) ) { if ( is_array($order_by) ) { foreach($order_by as $val => $order) { $tmp .= '`' . $val . '` ' . $order . ', '; } $order_by = ' ORDER BY ' . preg_replace( "/, $/" , "" , $tmp); } else { $order_by = ' ORDER BY ' . $order_by . ' DESC'; } } // Set up the LIMIT $tmp = ''; if ( !empty($limit) ) { if ( is_array($limit) ) { foreach ($limit as $num) { $tmp .= $num . ', '; } $limit = ' LIMIT ' . preg_replace( "/, $/" , "" , $tmp); } else { $limit = ' LIMIT ' . $limit; } } $query = 'SELECT ' . $columns . ' FROM `' . $table . '`' . $where . $order_by . $limit; // Return query string for debugging purposes or just do the query if ( $debug ) { return $query; } else { // Perform the query return $this->query($query); } } /** * insert(): Insert data into a table * @param string $table Table to insert into * @param array $data The column name & data * @param boolean $slashes True if you want to add 's to the string * @returns boolean True if successful, false if not. */ function insert($table, $data, $slashes = false) { if ( $table == '' || $data == '' ) { return; } $fields = ''; $values = ''; foreach($data as $key => $val) { $fields .= '`' . $key . '`, '; $val = ( $slashes == true ) ? addslashes($val) : $val; $values .= "'" . addslashes( stripslashes($val) ) . "'" . ', '; } $fields = preg_replace( "/, $/" , "" , $fields); $values = preg_replace( "/, $/" , "" , $values); mysql_query('INSERT INTO `' . $table . '` (' . $fields . ') VALUES (' . $values . ')', $this->handler) or $this->throw_error(mysql_error(), __LINE__); } /** * update(): Update data into a table * @param string $table Table to insert into * @param array $data The column name & data * @param boolean $slashes True if you want to add 's to the string * @returns boolean True if successful, false if not. */ function update($table, $data, $where, $slashes = false) { if ( $table == '' || $data == '' || $where == '') { return; } $string = ''; $tmp = ''; foreach($data as $key => $val) { $string .= '`' . $key . "` = '" . $this->escape_str($val) . "', "; } $string = preg_replace( "/, $/" , "" , $string); if ( is_array($where) ) { foreach ($where as $key => $val) { $tmp .= $key . " = '" . $this->escape_str($val) . "' AND "; } $where = preg_replace( "/AND $/" , "" , $tmp); } else { $where = $where; } mysql_query('UPDATE `' . $table . '` SET ' . $string . ' WHERE ' . $where, $this->handler) or $this->throw_error(mysql_error(), __LINE__); } /** * delete(): Delete a row from a table * @param string $table Table to insert into * @param array $data The column name & data */ function delete($table, $data) { if ( $table == '' || $data == '') { return; } mysql_query('DELETE FROM `' . $table . '` WHERE `' . key($data) . "` = '" . $data[key($data)] . "'", $this->handler) or $this->throw_error(mysql_error(), __LINE__); } /** * query(): Query from MySQL. * @param string $query The MySQL query * @returns MySQL resource * * Warning: This function is only ment to be called manually if being used in * a development environment because it does not do any input validation. */ function query($query) { if ( $query == '') { return; } $this->result = mysql_query($query, $this->handler) or $this->throw_error(mysql_error(), __LINE__); $this->num_rows = mysql_num_rows($this->result); if ($this->num_rows > 1) { $tmp_array = array(); while ($row = mysql_fetch_assoc($this->result)) { array_push($tmp_array, $row); } $this->result = $tmp_array; return $this->result; } else { $this->result = mysql_fetch_assoc($this->result); return $this->result; } } function escape_str($string) { if ( is_array($string) ) { foreach($string as $key => $val) { $str[$key] = $this->escape_str($val); } return $str; } if ( function_exists('mysql_escape_string') ) { return mysql_escape_string( stripslashes($string) ); } else { return addslashes( stripslashes($string) ); } } /** * close_connection(): Close connection to MySQL */ function db_close(){ mysql_close($this->handler); } } ?>
PHP 5 Email Class
Jan 25th
Here is an example class that you can use to send email to users. It includes a properly formatted email body meta information that should avoid most spam filters.
<?php /**************************************************** * * File: class.email.php * Purpose: Email Class * Author: Matt West, http://mattdanger.net * Date: January 25, 2007 * ****************************************************/ class Email { // Public variables public $recipient; public $sender; public $reply_to; public $subject; public $body; public $cc; public $bcc; public $headers; /** * Constructor: Read in $file. * @param string $to Recipient * @param array $from "Sender's Name" => "Sender's Email" * @param string $subject Email subject * @param string $body Email body */ public function __construct ($to, $from = array(), $subject, $body) { // Set receiver $this->receiver($to); // Set sender if ( !empty($from) ) { $this->sender = array( key($from) => $from[ key($from) ] ); } else { $this->sender = array( $_SERVER['SERVER_ADMIN'] => $_SERVER['SERVER_ADMIN'] . '@' . $_SERVER['SERVER_NAME'] ); } // Set subject $this->subject($subject); // Set body $this->body($body); } /** * receiver(): Set receiver of the email * @param array $send_to List of Names & Email addresses */ public function receiver ($send_to) { foreach ($send_to as $name => $email) { if ( $this->email_is_valid($email) ) { if ( !is_numeric($name) ) { $this->recipient = ucfirst($name) . ' <' . $email . '>'; } else { $this->recipient = $email; } $this->recipient .= ', '; } else { exit( $email . " is not a valid email address." ); } } $this->recipient = preg_replace('/, $/', '', $this->recipient); } /** * sender(): Set send of the email * @param array $sender Name & Email address */ public function sender ($sender) { if ( email_is_valid($sender[0]) ) { if ( !is_numeric( key($sender) ) ) { $this->sender = ucfirst( key($sender) ) . ' <' . $email . '>'; } else { $this->sender = $sender[0]; } } else { exit( $sender[0] . " is not a valid email address." ); } } /** * reply_to(): Set send of the email * @param array $reply_to Name & Email address */ public function reply_to ($reply_to) { if ( email_is_valid($reply_to[0]) ) { if ( !is_numeric( key($reply_to) ) ) { $this->reply_to = ucfirst( key($reply_to) ) . ' <' . $email . '>'; } else { $this->reply_to = $sender[ key($reply_to) ]; } } else { exit( $reply_to[0] . " is not a valid email address." ); } } /** * cc(): Set Cc of the email * @param array $cc List of Names & Email addresses */ public function cc ($cc) { foreach ($cc as $name => $email) { if ( email_is_valid($email) ) { if ( !is_numeric($name) ) { $this->cc = ucfirst($name) . ' <' . $email . '>'; } else { $this->cc = $email; } $this->cc .= ', '; } else { exit( $email . " is not a valid email address." ); } } $this->cc = preg_replace('/, $/', '', $this->to); } /** * bcc(): Set Bcc of the email * @param array $bcc List of Names & Email addresses */ public function bcc ($bcc) { foreach ($bcc as $name => $email) { if ( !is_numeric($name) ) { $this->bcc = ucfirst($name) . ' <' . $email . '>'; } else { $this->bcc = $email; } $this->bcc .= ', '; } $this->bcc = preg_replace('/, $/', '', $this->to); } /** * set_headers(): Set email headers */ public function set_headers () { $this->set_from(); $this->headers = "MIME-Version: 1.0rn" . "From: ". $this->sender . "rn" . "To: " . $this->recipient . "rn"; if ( !empty($this->reply_to) ) $this->headers .= "Reply-To: " . $this->reply_to . "rn"; if ( !empty($this->cc) ) $this->headers .= "Cc: " . $this->cc . "rn"; if ( !empty($this->bcc) ) $this->headers .= "Bcc: " . $this->bcc . "rn"; $this->headers .= "X-Priority: 1rn" . "X-Mailer: PHP/" . phpversion() . "rn" . "Content-type: text/html; charset=iso-8859-1rn"; } /** * subject(): Sets the subject of the email. * @param string $subject The subject message. */ public function subject ($subject) { // Strip any newlines $this->subject = str_replace('n', '', $subject); } /** * body(): Sets the body message of the email. * @param string $body The body message. */ public function body ($body) { $this->body = $body; } /** * send(): Send the email * @returns boolean True if successful, false if not. */ public function send () { if (mail ( $this->recipient, $this->subject, $this->body, $this->headers)) { return true; } else { return false; } } /** * email_is_valid(): Check whether email address is valid * @param string $email Email address to check * @returns boolean True if address is valid, false if not. */ private function email_is_valid ($email) { if (eregi("^[_.0-9a-z-]+@([0-9a-z][0-9a-z-]+.)+[a-z]{2,3}$", $email)) { return true; } else { return false; } } } ?>
PHP Login Class
Jan 15th
Here is an example of a PHP 4.2 Login class that you can use when writing your own CMS.
<?php /**************************************************** * * File: class.login.php * Author: Matt West, 2007 * Purpose: Login class * *****************************************************/ class Login { // Public variables var $username; var $cookie; function Login() { } /** * login(): Authenticate the user's username & password * @param string $username User's username in the DB * @param string $password User's password in the DB * @param object $DB MySQL database class object * @param string $table Table to query from * @returns bool True if login successful, false if not */ function login ($username, $password, $DB, $table = 'users') { $this->username = $username; $DB->query("SELECT `username`, `salt`, `password` FROM `$table` WHERE `username` = '$username' LIMIT 1"); return ( $DB->result['password'] == sha1($DB->result['salt'] . $password) ) ? true : false ; } /** * login_admin(): Authenticate the an admin's username & password * @param string $username User's username in the DB * @param string $password User's password in the DB * @param object $DB MySQL database class object * @param string $table Table to query from * @returns bool True if login successful, false if not */ function login_admin ($username, $password, $DB, $table = 'users') { $this->username = $username; $DB->query("SELECT `username`, `salt`, `password`, `user_level` FROM `$table` WHERE `username` = '$username' LIMIT 1"); if ( ( $DB->result['password'] == sha1($DB->result['salt'] . $password) ) && ( $DB->result['user_level'] ) == '3') { return true; } else { return false; } } /** * set_cookie(): Set the user's cookie * @param string $cookie_name Name of the cookie * @param object $DB MySQL database class object * @param bool $remember Whether to set for 30 days or just this session * @param string $table Table to query from * @returns bool True if cookie was sent, false if not */ function set_cookie ($cookie_name, $DB, $remember = true, $table = 'users') { $expiration = ( $remember ) ? ( time() + (60 * 60 * 24 * 30) ) : null ; $DB->query("SELECT * FROM `$table` WHERE `username` = '" . $this->username . "' LIMIT 1"); return ( setcookie($cookie_name, base64_encode( $DB->result['username'] . ":" . $DB->result['id'] . ":" . $DB->result['user_level'] . ":" . $DB->result['first_name'] . ":" . $DB->result['last_name'] ), $expiration) ) ? true : false ; } /** * is_logged_in(): Check if user is currently logged in * @param string $cookie_name Name of the cookie * @returns bool True if cookie was sent, false if not */ function is_logged_in ($cookie_name) { $this->cookie = explode(':', base64_decode($_COOKIE[$cookie_name]) ); } /** * is_admin_logged_in(): Check if user is currently logged in * @param string $cookie_name Name of the cookie * @returns bool True if cookie was sent, false if not */ function is_admin_logged_in ($cookie_name) { if ( isset($_COOKIE[$cookie_name]) ) { $this->cookie = explode(':', base64_decode($_COOKIE[$cookie_name]) ); if ( $this->cookie[2] == 3 ) { return true; } else { return false; } } else { return false; } } /** * logout(): Clear the user's cookie * @param string $cookie_name Name of the cookie */ function logout ($cookie_name) { setcookie($cookie_name, ''); } } ?>
PHP photo resizing class example
Jan 12th
Here is an example of a PHP class that manages simple photo resizing and saving using the GD library.
<?php /**************************************************** * * Purpose: Photo Resizing Class * Author: Matt West, http://mattdanger.net * Date: January 12th, 2007 * ****************************************************/ class Photo { // Private variables private $orig_image; private $portrait_frame; // 1 = Landscape, 2 = Portrait private $image_size; private $src_width; private $src_height; private $src_image; private $dest_height; private $dest_width; private $dest_image; private $ratio; private $scale; // Public variables public $allowed_types = array('jpeg', 'jpg', 'png', 'gif'); /** * Constructor: Contruct the photo object * @param array $photo Array of photo data, usually $_FILES['photo_name'] */ public function __construct($photo) { $this->photo($photo); } /** * photo(): Create new image resource * @param string $photo Array of photo data */ public function photo($photo) { $this->orig_image = $photo; // Confirm photo is an allowed type; if ( !in_array(ereg_replace('image/', '', $this->orig_image['type']), $this->allowed_types) ) { exit("Photo type is '" . $this->orig_image['type'] . "' which is not allowed"); } // Get image X and Y values in order to determine photo framing $this->image_size = getimagesize($this->orig_image['tmp_name']); $this->src_width = $this->image_size[0]; $this->src_height = $this->image_size[1]; $this->dest_height = 0; // 1 = Landscape, 2 = Portrait $this->photo_framing = ($this->src_width > $this->src_height) ? 1 : 2 ; // Calculate the ratio at which to resize based on the dimentions $this->ratio = $this->src_width / $this->src_height; } /** * scale_to_width(): Sets the width of the resize value * @param int $width Width in pixels * @returns boolean Returns true if successful, false if it was not. */ public function scale_to_width($width) { // Set the destination width value $this->dest_width = $width; // Calculate the value at which to scale the image $this->scale = ($this->ratio) ? $this->dest_width / $this->src_width : $this->dest_height / $this->src_height; if ($this->scale_image()) { return true; } else { return false; } } /** * scale_to_height(): Sets the height of the resize value * @param int $height Height in pixels * @returns boolean Returns true if successful, false if it was not. */ public function scale_to_height($height) { // Set the destination height value $this->dest_height = $height; // Calculate the ratio at which to resize based on the dimentions $this->ratio = $this->src_width / $this->src_height; // Calculate the value at which to scale the image $this->scale = ($this->ratio) ? $this->dest_height / $this->src_height : $this->dest_width / $this->src_width ; if ($this->scale_image()) { return true; } else { return false; } } /** * scale_image(): Resize image proportionally * @returns boolean Returns true if successful, false if it was not. */ private function scale_image () { if ($this->scale > 1) { exit ("The scale width supplied is larger than the original image. Please try a smaller number."); } $this->dest_width = $this->src_width * $this->scale; $this->dest_height = $this->src_height * $this->scale; if ( $this->dest_width >= $this->src_width && $this->dest_width >= $this->src_width ) $this->scale = 1; $this->src_image = imagecreatefromjpeg ($this->orig_image['tmp_name']); $this->dest_image = imagecreatetruecolor ($this->src_width * $this->scale, $this->src_height * $this->scale); if ( imagecopyresampled( $this->dest_image, $this->src_image, 0, 0, 0, 0, $this->dest_width, $this->dest_height, $this->src_width, $this->src_height) ) { return true; } else { return false; } } /** * save_photo(): Saves the resized photo * @param string $filename The path and filename to save the file (ex: "/path/to/image.jpg") * @param int $compression A number from 1-100 representing the level of compression. 100 is the least compression. * @returns boolean Returns true if save was successful, false if it was not. */ public function save_photo ($filename, $compression = 80) { if ( imagejpeg($this->dest_image, $filename, $compression) ) { $return_value = true; } else { $return_value = false; } imagedestroy($this->src_image); imagedestroy($this->dest_image); return $return_value; } } ?>










