Image Resizing Script required - a better one...

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

why not add a fourth parameter to resizeSingleImage and pass that value in?
Remember, you are suggesting an idea, on an area I still know extremely little about.
I understand that I shouldn't be copying and pasting. But I don't know how to get those sizes to be used everywhere, without doing that.
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

simonmlewis wrote:
why not add a fourth parameter to resizeSingleImage and pass that value in?
Remember, you are suggesting an idea, on an area I still know extremely little about.
I do recognize that but I hope that as we work through this together you walk away understanding more about it. Otherwise I'd just have given you the answer.
simonmlewis wrote:I understand that I shouldn't be copying and pasting. But I don't know how to get those sizes to be used everywhere, without doing that.
Look at what we've done with the other functions. Inside the function signature (ie. between the parentheses after the function name) we list which variables it will accept and what they will be called within the scope of that function. This is why you pass $row->image into getSrcSet but refer to is as $filename within the function. Inside resizeSingleImage, we're currently accepting the filename of the original image, the suffix to append to the new file, and the width we want the new file to be. Those are $original, $suffix, and $width, respectively. You could add a fourth variable to that list, call it something meaningful like $save_path, and then add that argument when you're calling the function.

So we'd now have something like:

Code: Select all

// Resizes a single image to a specific size and with a specific suffix
function resizeSingleImage($original, $suffix, $width, $save_path)
{
    $imagine = new Imagine\Gd\Imagine();

    $target_directory = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $save_path;
    $pathinfo = pathinfo($original);

    // Open the uploaded image with the Imagine library
    $image = $imagine->open($target_directory . DIRECTORY_SEPARATOR . $original);

    // Get the size of the original image
    $box = $image->getSize();

    $ratio = $width / $box->getWidth();
    $scaled_box = $box->scale($ratio);
    $new_filename = "{$pathinfo['filename']}_{$suffix}.{$pathinfo['extension']}";

    $options = getImageOptions($pathinfo['extension']);
    $image->resize($scaled_box)->save($target_directory . DIRECTORY_SEPARATOR . $new_filename, $options);
}
Then from inside our getSrcSet function, which is where we call resizeSingleImage, we pass along $actualfolder like so:

Code: Select all

function getSrcSet($filename, $resize_type = 'wide')
{
    switch($resize_type) {
        case 'stockbanners':
            $actualfolder = 'stockbanners';
            break;
        default:
            $actualfolder = 'pages';
            break;
    }

    $root_directory = dirname(__DIR__);
    $images_directory = DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $actualfolder;
    $images_path = "/images/{$actualfolder}"; // Still need this for srcset output
    $source_directory = $root_directory . $images_directory;
    $srcset = [];

    $widths = getWidths($resize_type);

    if (file_exists($source_directory . DIRECTORY_SEPARATOR . $filename)) {
        $basename = pathinfo($filename, PATHINFO_FILENAME);
        $extension = pathinfo($filename, PATHINFO_EXTENSION);

        foreach ($widths as $size => $width) {
            $fullname = $basename . '_' . $size . '.' . $extension;
            if (file_exists($source_directory . DIRECTORY_SEPARATOR . $fullname)) {
                $srcset[] = "{$images_path}/{$fullname} {$size}w";
            } else {
                resizeSingleImage($filename, $size, $width, $actualfolder);
                $srcset[] = "{$images_path}/{$fullname} {$size}w";
            }
        }
    }

    return implode(', ', $srcset);
}
Does that make sense?
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

Two areas here I don't understand.
One I have seen a fair bit, but still don't get it.
1) function getSrcSet($filename, $resize_type = 'wide')
Which is that pinpointing the "wide" case??
2) $save_path - where is it getting that from?

I am assuming that from a function further up the page, you can attach that to a function further down, and that is what 2) is doing??
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

This is it at the moment.
I keep seeing differences between yours and mine. I've never had code before that has $variable,$variable2,$variable3... in a row like that. So trying to get a grasp on how each area uses that sort of string.

This is still product big errors.
When I get errors in my own code I tend to know the cause and go straight to it. I am assuming with 'functions' you are very similar.
I have never used functions, but this lesson is helping me a lot. Just trying to grasp it, learn it, and see the magic it can do.

Code: Select all

<?php
require_once '/vendor/autoload.php';

// Returns required quality settings for Imagine based on image's extension
function getImageOptions($extension)
{
    switch ($extension) {
        case 'jpg':
        case 'jpeg':
            $options = ['jpeg_quality' => 97];
            break;
        case 'png':
            $options = ['png_compression_level' => 8];
            break;
        default:
            $options = [];
    }

    return $options;
}

function getSrcSet($filename, $resize_type = 'wide')
{
  switch($resize_type) {
      case 'stockbanners':
      $actualfolder = 'stockbanners';
      break;
      default:
      $actualfolder = 'pages';
      break;
     }
    $root_directory = dirname(__DIR__);
    $images_directory = DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $actualfolder;
    $images_path = "/images/{$actualfolder}"; // Still need this for srcset output
    $source_directory = $root_directory . $images_directory;
    $srcset = [];

    $widths = getWidths($resize_type);

    if (file_exists($source_directory . DIRECTORY_SEPARATOR . $filename)) {
        $basename = pathinfo($filename, PATHINFO_FILENAME);
        $extension = pathinfo($filename, PATHINFO_EXTENSION);

        foreach ($widths as $size => $width) {
            $fullname = $basename . '_' . $size . '.' . $extension;
            if (file_exists($source_directory . DIRECTORY_SEPARATOR . $fullname)) {
                $srcset[] = "{$images_path}/{$fullname} {$size}w";
            } else {
                resizeSingleImage($filename, $size, $width, $actualfolder);
                $srcset[] = "{$images_path}/{$fullname} {$size}w";
            }
        }
    }

    return implode(', ', $srcset);
}

// Returns an array of widths, keyed by target screen size
function getWidths($resize_option)
{
    switch($resize_option) {
        case 'categories':
        case 'products':
        case 'stockbanners':
            $widths = [
                '475' => 237,
                '768' => 384,
                '1920' => 451,
            ];
            break;
        case 'wide':
            $widths = [
                '475' => 237,
                '768' => 384,
                '1920' => 950,
            ];
            break;
        case 'desktopslide':
            $widths = [
                '475' => 475,
                '768' => 768,
                '1920' => 1920,
            ];
            break;
        default:
            $widths = [];
            break;
    }

    return $widths;
}

// Takes input from a form post and saves original image plus all required resizes
function resizeImage($path_to_file, $file_name, $resize_type)
{
    $imagine = new Imagine\Gd\Imagine();
    
  switch($resize_type) {
      case 'stockbanners':
      $actualfolder = 'stockbanners';
      break;
      default:
      $actualfolder = 'pages';
      break;
     }
    
    $target_directory = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $actualfolder;
    $pathinfo = pathinfo($file_name);
    $prefix = time();

    // Open the uploaded image with the Imagine library
    $image = $imagine->open($path_to_file);

    // Save the original
    $options = getImageOptions($pathinfo['extension']);
    $path_to_original = $target_directory . DIRECTORY_SEPARATOR . "{$prefix}{$pathinfo['basename']}";
    $image->save($path_to_original, $options);

    // Resize

    // Get the size of the original image
    $box = $image->getSize();

    // Get the sizes we need
    $widths = getWidths($resize_type);

    // Create resized images
    foreach ($widths as $key => $width) {
        $ratio = $width / $box->getWidth();
        $scaled_box = $box->scale($ratio);
        $new_filename = "{$prefix}{$pathinfo['filename']}_{$key}.{$pathinfo['extension']}";

        // Re-open the original for scaling so we're not creating a larger image from a smaller
        $source = $imagine->open($path_to_original);
        $source->resize($scaled_box)->save($target_directory . DIRECTORY_SEPARATOR . $new_filename, $options);
    }
}

// Resizes a single image to a specific size and with a specific suffix
function resizeSingleImage($original, $suffix, $width, $save_path)
{
    $imagine = new Imagine\Gd\Imagine();

    $target_directory = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . $save_path;
    $pathinfo = pathinfo($original);

    // Open the uploaded image with the Imagine library
    $image = $imagine->open($target_directory . DIRECTORY_SEPARATOR . $original);

    // Get the size of the original image
    $box = $image->getSize();

    $ratio = $width / $box->getWidth();
    $scaled_box = $box->scale($ratio);
    $new_filename = "{$pathinfo['filename']}_{$suffix}.{$pathinfo['extension']}";

    $options = getImageOptions($pathinfo['extension']);
    $image->resize($scaled_box)->save($target_directory . DIRECTORY_SEPARATOR . $new_filename, $options);
}

?>
[text]Fatal error: Uncaught exception 'Imagine\Exception\InvalidArgumentException' with message 'File C:\xampp\phpMyAdmin\site-wide\images\stockbanners\ does not exist.' in C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Image\AbstractImagine.php:72 Stack trace: #0 C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Gd\Imagine.php(86): Imagine\Image\AbstractImagine->checkPath('C:\\xampp\\phpMyA...') #1 C:\xampp\phpMyAdmin\site-wide\functions\functionConsumerResize.php(148): Imagine\Gd\Imagine->open('C:\\xampp\\phpMyA...') #2 C:\xampp\phpMyAdmin\site-wide\functions\functionConsumerResize.php(49): resizeSingleImage(NULL, 475, 237, 'stockbanners') #3 C:\xampp\phpMyAdmin\site-wide\includes\page.inc(310): getSrcSet(NULL, 'stockbanners') #4 C:\xampp\phpMyAdmin\site-wide\index.php(42): include('C:\\xampp\\phpMyA...') #5 C:\xampp\phpMyAdmin\site-wide\index.php(437): getPage(Object(PDO)) #6 {main} thrown in C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Image\AbstractImagine.php on line 72[/text]
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

simonmlewis wrote:Two areas here I don't understand.
One I have seen a fair bit, but still don't get it.
1) function getSrcSet($filename, $resize_type = 'wide')
Which is that pinpointing the "wide" case??
Can you please clarify what you mean here? $resize_type is the name of the variable within the scope of the function, and 'wide' is its default value -- what will be used if we do not pass a second argument. Not sure if that answers your question or not.
simonmlewis wrote:2) $save_path - where is it getting that from?

I am assuming that from a function further up the page, you can attach that to a function further down, and that is what 2) is doing??
We're defining it. When you define a function (eg. function someName(arguments go here)) you specify how many parameters it will accept and what they will be called. The function cannot see out into the larger application. The only things it knows about are the things you explicitly pass in. If you need to see more of the outside, you need to accept and pass in more arguments. That is exactly what is happening here. Initially we were unconcerned about a save path as everything was being saved to the same directory. Now we have new requirements so the hard-coded directory is no longer adequate. To work around this, we modify the function signature to accept an additional parameter and we then use that parameter within the function itself when declaring the full path to which images will be saved.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

Stack traces. These are indispensable at identifying where things have gone wrong. Cleaned up a little for the sake of readability, here's what you just posted.

Code: Select all

Fatal error: Uncaught exception 'Imagine\Exception\InvalidArgumentException' with message 'File C:\xampp\phpMyAdmin\site-wide\images\stockbanners\ does not exist.' in C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Image\AbstractImagine.php:72

Stack trace:

#0 C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Gd\Imagine.php(86): Imagine\Image\AbstractImagine->checkPath('C:\\xampp\\phpMyA...')
#1 C:\xampp\phpMyAdmin\site-wide\functions\functionConsumerResize.php(148): Imagine\Gd\Imagine->open('C:\\xampp\\phpMyA...')
#2 C:\xampp\phpMyAdmin\site-wide\functions\functionConsumerResize.php(49): resizeSingleImage(NULL, 475, 237, 'stockbanners')
#3 C:\xampp\phpMyAdmin\site-wide\includes\page.inc(310): getSrcSet(NULL, 'stockbanners')
#4 C:\xampp\phpMyAdmin\site-wide\index.php(42): include('C:\\xampp\\phpMyA...')
#5 C:\xampp\phpMyAdmin\site-wide\index.php(437): getPage(Object(PDO))
#6 {main} thrown in C:\xampp\phpMyAdmin\site-wide\vendor\imagine\imagine\lib\Imagine\Image\AbstractImagine.php on line 72
The first thing that jumps out at me is this line:
[text]#3 C:\xampp\phpMyAdmin\site-wide\includes\page.inc(310): getSrcSet(NULL, 'stockbanners')[/text]
NULL? That's interesting. I'm assuming you're passing in $row->image, but we're getting nothing. Does this product not have an associated image? That's liable to happen at some point and we need to guard against that. Any thoughts on how we might accomplish that?
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

It won't be that. It's only calling that from the database if it finds one is there. So it won't ever be a case of trying when there isn't one there.

However, it might be possible that in my copying over, I've failed to copy that image. I'll check on that when I get home. Not sure what else would cause a NULL in there.
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

Found it.

Code: Select all

if ($row->stockbanner == "yes")
{

$file = "/images/stockbanners/$rowprod->stockbanner";
$ext = pathinfo($file, PATHINFO_EXTENSION);
$filename = pathinfo($file, PATHINFO_FILENAME);
  
echo "<a href='$row->url'>";
$image = $rowprod->stockbanner; // <<< this was $row->image
$srcset = getSrcSet($image, 'stockbanners');
echo "<img src='/images/stockbanners/$image' srcset='{$srcset}' alt='$row->freetext'/>";
}
else
{
$image = $row->image;
$srcset = getSrcSet($image, 'stockbanners');
echo "<img src='/images/pages/$image' srcset='{$srcset}' alt='$row->freetext'/>";
}
It works and resizes perfectly.
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

I am gradually rolling this out on our other admin and home pages, though I need to tweak some, where we give an option to "replace image".

But I have a query about Composer installation no a server - if you know about that?
Our hosts confirm they have installed it, but we are getting an error in the log.

Code: Select all

$widths = [
    '475' => 237,
    '768' => 384,
    '1920' => 451,
];
On this first [.

I looked at the folder structure in FTP, and no vendor folder has been created. Our control panel is Plesk 12.
Why would it not show that vendor folder? Does it mean they haven't installed Composer correctly??
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

What does the error say? Are you sure it's related to Composer? Also, Composer is generally run from the command line. If it's shared hosting and you don't have access to the command line, you'll want to talk to them to get that set up or you may need to upload your vendor directory from your development server, which isn't ideal.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

So if I add my composer 'vendor' folder, that will likely fix it?
Or do I think I need to ask them for the full string of that?

Also, trying to ascertain the best method here.
I have added the script to delete the Stock Banners in the two admin areas (we show Stock Banners in one big list, and in each individual product).
Is it best when uploading a new one to upload the others too, or leave that to the function, so it's all done from one easy place?

What I am thinking here, is that with the Product Photos, the thumbnails - using that function, adding in another version for "product", that uses /productphotos/small, to store them, and it just adds them on the fly (like Wordpress does).
And when admin deletes an image, it deletes all four.

Sounds like a more effective and manageable solution.
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

simonmlewis wrote:So if I add my composer 'vendor' folder, that will likely fix it?
Or do I think I need to ask them for the full string of that?
I would talk to your hosting provider and see what they can recommend. That they have Composer installed is encouraging. Wouldn't make much sense to me to have it installed yet have no way for your users to leverage it. I haven't worked with shared hosting in a long while, so I don't know what the most common practice is. If it's a VPS and you have shell access, then you can just log in and run composer install yourself.
simonmlewis wrote:Also, trying to ascertain the best method here.
I have added the script to delete the Stock Banners in the two admin areas (we show Stock Banners in one big list, and in each individual product).
Is it best when uploading a new one to upload the others too, or leave that to the function, so it's all done from one easy place?

What I am thinking here, is that with the Product Photos, the thumbnails - using that function, adding in another version for "product", that uses /productphotos/small, to store them, and it just adds them on the fly (like Wordpress does).
And when admin deletes an image, it deletes all four.

Sounds like a more effective and manageable solution.
I would try as much as possible to create the thumbnails on upload and delete the whole lot when you delete the original image. Sure, we now have provisions to resize on the fly if/as needed, but that also causes some rendering delay for the user. Better the site administrator should have to wait a second or two than a customer should.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

Agreed.

So we now have an error on our sandbox server. And it's in the most bizarre please:

Code: Select all

if (isset($resize))
{
  if ($resize == "categories" || $resize == "products")
  {
    // An array of widths, keyed by target screen size
$widths = [
    '475' => 237,
    '768' => 384,
    '1920' => 451,
];
.........
It doesn't like that first [.
I know that is just an array. We are running 5.3.3. Now where there are two versions of 3.3 and one didn't use those. But surely it's just array code.
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
User avatar
Celauran
Moderator
Posts: 6427
Joined: Tue Nov 09, 2010 2:39 pm
Location: Montreal, Canada

Re: Image Resizing Script required - a better one...

Post by Celauran »

Short array syntax wasn't introduced until 5.4. You'll need array() for 5.3. Also, you really shouldn't be running 5.3 any longer.
simonmlewis
DevNet Master
Posts: 4435
Joined: Wed Oct 08, 2008 3:39 pm
Location: United Kingdom
Contact:

Re: Image Resizing Script required - a better one...

Post by simonmlewis »

ahhhhhhhhhhhhh
So for now, how do I edit the $widths to accept it on this server until we update?
Love PHP. Love CSS. Love learning new tricks too.
All the best from the United Kingdom.
Post Reply