I have a form on "add-category.php" that enables the admin to create a category and doing so includes uploading an image to a folder and inserts its file name and relevant text into the database. The script worked well with mysql_, but now it stopped working after I upgraded my "functions.php" to PDO. The issue is that the image cannot upload.

Warning: imagejpeg(/home/ script-directory/products/images/category/ec27a92192042fdc049e54477649fb30.jpg): failed to open stream: No such file or directory in /home/script-directory/includes/functions.php on line 385

This is my "add-category.php":

<?php

require_once '../../includes/config.php';

if(isset($_POST['txtName']))

{

    $categoryName = $_POST['txtName'];

    $categoryMtitle = $_POST['metaTitle'];

    $categoryMkey = $_POST['metaKey'];

    $categoryMdes = $_POST['metaDesc']; 

    $categoryDesc = $_POST['mtxDesc'];

    $imgName   = $_FILES['fleImage']['name'];

    $tmpName   = $_FILES['fleImage']['tmp_name'];

    // we need to rename the image name just to avoid

    // duplicate file names

    // first get the file extension

    $ext = strrchr($imgName, ".");

    // then create a new random name

    $newName = md5(rand() * time()) . $ext;

    // the category image will be saved here

    $imgPath = ALBUM_IMG_DIR . $newName;

    // resize all category image

    $result = createThumbnail($tmpName, $imgPath, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);

    if (!$result) {
        echo "Error uploading file";
        exit;

    }
    if(!$query = "INSERT INTO prod_cat (cat_name, cat_metatitle, cat_metakeywords, cat_metadescription, cat_description, cat_image, cat_date) 

              VALUES (:categoryName, :categoryMtitle, :categoryMkey, :categoryMdes, :categoryDesc, :newName, NOW())")
    {
        adminapologize("Error, adding product category failed.");         
    }

    $params = array(':categoryName' => $categoryName, ':categoryMtitle' => $categoryMtitle, ':categoryMkey' => $categoryMkey, ':categoryMdes' => $categoryMdes, ':categoryDesc' => $categoryDesc, ':newName' => $newName);

        var_dump($params);
        exit;

    // the category is saved, go to the category list 

    echo "<script>window.location.href='index.php?page=list-category';</script>";

    exit;

}

    // include add category template
    include("templates/add-category_template.php");

?>

Doing var_dump($params);
exit;
outputs this code:

array (size=6)
  ':categoryName' => string 'Balls' (length=5)
  ':categoryMtitle' => string 'Corporate Gift balls' (length=20)
  ':categoryMkey' => string 'ball, balls' (length=11)
  ':categoryMdes' => string 'Buy corporate gift items like balls' (length=35)
  ':categoryDesc' => string '<p>Buy corporate gift items like balls</p>' (length=42)
  ':newName' => string 'f59723c81fe0c4235b57ad338e981bff.jpg' (length=36)

Here is the definition of image location in "config.php":

 // APP_ROOT will always be set to 2 directorys up from the location of this file
    define('APP_ROOT', dirname(dirname(__FILE__)) . '/');
    // a category can have an image used as thumbnail
    // we save the category image here
    define('ALBUM_IMG_DIR', APP_ROOT . 'products/images/category/');
// all images inside an category are stored here
define('GALLERY_IMG_DIR', APP_ROOT . 'products/images/gallery/');



 In the "functions.php", I have this code regarding PDO and image upload:
     /**

    * Executes SQL statement, possibly with parameters, returning

    * a pdo statement object on success, handling and halting execution on error.

    */

    function query($sql, $parameters = null)

    {

        static $pdo; // define the var as static so that it will persist between function calls



    try

    {

        // if no db connection, make one

        if (!isset($pdo))

        {

            // connect to database



            // you should set the character encoding for the connection



            $pdo = new PDO("mysql:dbname=" . DB_NAME . ";host=" . DB_SERVER, DB_USERNAME, DB_PASSWORD);

            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // set the error mode to exceptions

            $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false); // turn emulated prepares off

            $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC); // set default fetch mode to assoc so that you don't have to explicitly list the fetch mode every place

        }



        if(empty($parameters)){

            // no bound inputs

            $stmt = $pdo->query($sql);

        } else {

            // has bound inputs

            $stmt = $pdo->prepare($sql);



             // you should use explicit bindValue() statements to bind any inputs, instead of supplying them as a parameter to the ->execute() method. the comments posted in your thread lists the reasons why.



            $stmt->execute($parameters);

        }



        }

        catch (Exception $e)

        {

            // all errors with the connection, query, prepare, and execute will be handled here



            // you should also use the line, file, and backtrace information to produce a detailed error message

            // if the error is due to a query, you should also include the $sql statement as part of the error message

            // if $pdo ($handle in your code) is set, it means that the connection was successful and the error is due to a query. you can use this to include the $sql in the error message.



            trigger_error($e->getMessage(), E_USER_ERROR);

            //exit; // note: E_USER_ERROR causes an exit, so you don't need an exit; here.

        }



            return $stmt; // if the query ran without any errors, return the pdo statement object to the calling code

        }



/*

    Upload an image and create the thumbnail. The thumbnail is stored 

    under the thumbnail sub-directory of $uploadDir.



    Return the uploaded image name and the thumbnail also.

*/

function uploadImage($inputName, $uploadDir)

{

    $image     = $_FILES[$inputName];

    $imagePath = '';

    $thumbnailPath = '';



    // if a file is given

    if (trim($image['tmp_name']) != '') {

        $ext = substr(strrchr($image['name'], "."), 1); 



        // generate a random new file name to avoid name conflict

        // then save the image under the new file name

        $imagePath = md5(rand() * time()) . ".$ext";

        $result    = move_uploaded_file($image['tmp_name'], $uploadDir . $imagePath);



        if ($result) {

            // create thumbnail

            $thumbnailPath =  md5(rand() * time()) . ".$ext";

            $result = createThumbnail($uploadDir . $imagePath, $uploadDir . 'thumbnail/' . $thumbnailPath, THUMBNAIL_WIDTH);



            // create thumbnail failed, delete the image

            if (!$result) {

                unlink($uploadDir . $imagePath);

                $imagePath = $thumbnailPath = '';

            } else {

                $thumbnailPath = $result;

            }   

        } else {

            // the image cannot be uploaded

            $imagePath = $thumbnailPath = '';

        }



    }





    return array('image' => $imagePath, 'thumbnail' => $thumbnailPath);

}



/*

    Create a thumbnail of $srcFile and save it to $destFile.

    The thumbnail will be $width pixels.

*/

function createThumbnail($srcFile, $destFile, $width, $quality = 75)

{

    $thumbnail = '';



    if (file_exists($srcFile)  && isset($destFile))

    {

        $size        = getimagesize($srcFile);

        $w           = number_format($width, 0, ',', '');

        $h           = number_format(($size[1] / $size[0]) * $width, 0, ',', '');



        $thumbnail =  copyImage($srcFile, $destFile, $w, $h, $quality);

    }



    // return the thumbnail file name on sucess or blank on fail

    return basename($thumbnail);

}



/*

    Copy an image to a destination file. The destination

    image size will be $w X $h pixels

*/

function copyImage($srcFile, $destFile, $w, $h, $quality = 75)

{

    $tmpSrc     = pathinfo(strtolower($srcFile));

    $tmpDest    = pathinfo(strtolower($destFile));

    $size       = getimagesize($srcFile);



    if ($tmpDest['extension'] == "gif" || $tmpDest['extension'] == "jpg")

    {

       $destFile  = substr_replace($destFile, 'jpg', -3);

       $dest      = imagecreatetruecolor($w, $h);

       //imageantialias($dest, TRUE);

    } elseif ($tmpDest['extension'] == "png") {

       $dest = imagecreatetruecolor($w, $h);

       //imageantialias($dest, TRUE);

    } else {

      return false;

    }



    switch($size[2])

    {

       case 1:       //GIF

           $src = imagecreatefromgif($srcFile);

           break;

       case 2:       //JPEG

           $src = imagecreatefromjpeg($srcFile);

           break;

       case 3:       //PNG

           $src = imagecreatefrompng($srcFile);

           break;

       default:

           return false;

           break;

    }



    imagecopyresampled($dest, $src, 0, 0, 0, 0, $w, $h, $size[0], $size[1]);



    switch($size[2])

    {

       case 1:

       case 2:

           imagejpeg($dest,$destFile, $quality);

           break;

       case 3:

           imagepng($dest,$destFile);

    }

    return $destFile;



}

Your help would be much appreciated. Thanks in advance.

Member Avatar
diafol

I can't see where you're running a query (or prepared statement). You have a SQL string, but nothing else.

The mysql query I originally had on add-category.php was this:

$query = "INSERT INTO prod_cat (cat_name, cat_metatitle, cat_metakeywords, cat_metadescription, cat_description, cat_image, cat_date) 
        VALUES ('$categoryName', '$categoryMtitle', '$categoryMkey', '$categoryMdes', '$categoryDesc', '$newName', NOW())";
mysql_query($query) or die('Error, add product category failed : ' . mysql_error());

Please, bear with me; I'm still struggling to get a handle on PDO prepared statement and this was my attempt at converting the above code:

if(!$query = "INSERT INTO prod_cat (cat_name, cat_metatitle, cat_metakeywords, cat_metadescription, cat_description, cat_image, cat_date) 
    VALUES (:categoryName, :categoryMtitle, :categoryMkey, :categoryMdes, :categoryDesc, :newName, NOW())")
{
    adminapologize("Error, adding product category failed.");         
}

$params = array(':categoryName' => $categoryName, ':categoryMtitle' => $categoryMtitle, ':categoryMkey' => $categoryMkey, ':categoryMdes' => $categoryMdes, ':categoryDesc' => $categoryDesc, ':newName' => $newName);

Can you please help?

Member Avatar
diafol

The PDO way would be something like this:

$stmt = $db->prepare($sql);
$stmt->execute($params);

The fetch method is up to you, e.g.

$data = $stmt->fetchAll();

@cereal, Thanks for the insight.

I updated the select query on add-category.php to this:

$query = "INSERT INTO admin (username, hash, email, phone) VALUES (:username, :hash, :email, :phone)";
$params = array(':username' => $username, 'hash' => $hash, ':email' => $email, ':phone' => $phone);

$result = query($query, $params);
$affected_rows = $result->rowCount();

Now all the data are inserted into the database, but the image upload and move to upload directory still fails. Any idea?

Member Avatar
diafol

I'm assuming that the failure is due to the original image manipulation routine.

There are a few "iffy" lines here.

Getting the extension and other bits of filename info should be left to pathinfo(). For example...

$fileinfo = pathinfo($_FILES[$inputName]['tmp_name']); 

$fileinfo['basename'] will give the full filename (name + extension)
$fileinfo['extension'] will give the extension
$fileinfo['filename'] will give the filename without the extension

Check for errors with $_FILES[$inputName]['error'] (should be = 0)

There are a few more but it looks as you are passing a file(name) to the imagejpeg() function, but it requires an image resource. The second optional parameter for this function takes a (storage) filename. Use this if you want to store a physical file, otherwise you will create and possibly output the image stream.

@diafol, thanks for your contribution.

Which file do you suggest I edit and which line? add-category.php or functions.php or config.php? Sorry, I'm just a learner.

Member Avatar
diafol

The issue is with what you're passing to imagejpeg(). Sorry done for today. Back tomorrow. Anybody else in the meantime?

I still don't get it. can you please tell me the exact file to edit and which line and a replacement code. Thanks in advance.

Member Avatar
diafol

Warning: imagejpeg(/home/ script-directory/products/images/category/ec27a92192042fdc049e54477649fb30.jpg): failed to open stream: No such file or directory in /home/script-directory/includes/functions.php on line 385

Can you confirm line 385?

Line 385 has this code:

imagejpeg($dest,$destFile, $quality);

The directory, category, exists in script-directory/products/images/category.

Member Avatar
diafol

Sorry mexabet, but those image function were doing my head in! :) So here's a simple class, that hopefully will be easier to use and debug.:

<?php
/**
 * Class UpThumb
 */
class UpThumb
{
    private $valid = false;
    private $getSize;
    private $newFilename;
    private $newThumbPath;
    private $newPath;
    private $file;
    private $allowedExt = ['gif', 'jpeg', 'jpg', 'png'];
    private $allowedExif = [1, 2, 3];
    private $ext;

    /**
     * @param $inputName
     * @param $uploadDir
     * @return $this|bool
     */
    public function uploadImage($inputName, $uploadDir)
    {
        //No error checking included - see http://php.net/manual/en/features.file-upload.php for info
        if (isset($_FILES[$inputName]) && $_FILES[$inputName]['error'] == 0) {
            $this->file = $_FILES[$inputName];
            $pathInfo = pathinfo($this->file['name']);
            $this->ext = strtolower($pathInfo['extension']);
            if ($this->checkAllowed($this->ext) && in_array(exif_imagetype($this->file['tmp_name']),$this->allowedExif)) {
                $this->getSize = getimagesize($this->file['tmp_name']);
                $this->newFilename = md5(rand() * time()) . "." . $this->ext;
                $this->newPath = $uploadDir . $this->newFilename;
                if (move_uploaded_file($this->file['tmp_name'], $this->newPath)) {
                    $this->valid = true;
                    return $this;
                }
            }
        }
        return false;
    }

    /**
     * @return array
     */
    public function getFilenames()
    {
        $ret = [];
        $ret['upload'] = $this->newPath;
        if($this->newThumbPath) $ret['thumb'] = $this->newThumbPath;
        return $ret;
    }

    /**
     * @param $thumbPathPrefix
     * @param null|int $w thumbnail width
     * @param null|int $h thumbnail height px
     * @param string $type gif|jpeg|jpg|png
     * @param int $quality 0-9 for png|0-100 for jpeg
     * @return $this
     */
    public function createThumb($thumbPathPrefix, $w = 100, $h = NULL, $type = 'jpg', $quality = 75)
    {
        $type = strtolower($type);
        $this->newThumbPath = $thumbPathPrefix . $this->newFilename;
        if($this->valid && (is_int($w) || is_int($h)) && $this->checkAllowed($type)) {

            list($tW, $tH) = $this->calcDimensions($w, $h);
            $thumbRes = imagecreatetruecolor($tW, $tH);

            switch($this->ext) {
                case 'gif':
                    $imgRes = imagecreatefromgif($this->newPath);
                    break;
                case 'jpeg':
                case 'jpg':
                    $imgRes = imagecreatefromjpeg($this->newPath);
                    break;
                case 'png':
                    $imgRes = imagecreatefrompng($this->newPath);
                    break;
            }

            imagecopyresampled($thumbRes, $imgRes, 0, 0, 0, 0, $tW, $tH, $this->getSize[0], $this->getSize[1]);

            switch($type) {
                case 'gif':
                    imagegif($thumbRes, $this->newThumbPath);
                    break;
                case 'jpeg':
                case 'jpg':
                    $quality = $this->checkQuality($quality, 0, 100);
                    imagejpeg($thumbRes, $this->newThumbPath, $quality);
                    break;
                case 'png':
                    $quality = $this->checkQuality($quality, 0, 9);
                    imagepng($thumbRes, $this->newThumbPath, $quality);
                    break;
            }
            return $this;
        }
    }

    /**
     * @param $type
     * @return bool
     */
    private function checkAllowed($type)
    {
        return in_array($type, $this->allowedExt);
    }


    /**
     * @param $w
     * @param $h
     * @return array
     */
    private function calcDimensions($w, $h)
    {
        if(is_int($w) && is_int($h)) return [$w, $h];
        if(is_int($w)) return [$w, $this->getSize[1] * $w / $this->getSize[0]];
        return [$this->getSize[0] * $h / $this->getSize[1], $h];
    }

    /**
     * @param $quality
     * @param $min
     * @param $max
     * @return null|int
     */
    private function checkQuality($quality, $min, $max)
    {
        if(!is_int($quality)){
            return NULL;
        } elseif($quality > $max) {
            return $max;
        } elseif($quality < $min) {
            return $min;
        } else {
            return $quality;
        }
    }
}


//Create Object
//Upload, Create Thumb and get Filenames
if(isset($_FILES['myfile'])) {
    $x = new UpThumb;
    $p = $x->uploadImage('myfile', 'upload/')->createThumb('thumbs/')->getFilenames();
}

?>


<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
</head>
<body>

<?php
    if(isset($p['upload'])) echo "UPLOADED:<br /> <img src = '{$p['upload']}' /> <p>{$p['upload']}</p>";
    if(isset($p['thumb'])) echo "THUMB:<br /> <img src = '{$p['thumb']}' /> <p>{$p['thumb']}</p>";
?>

  <form method="post" enctype="multipart/form-data">
      <input type="file" name="myfile" />
      <input type="submit" value="Go" />
  </form>

</body>
</html>

Works for me.

Thanks, I'll try it and see if I can integrate it successfully into my script.