Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

General error: could not call class constructor'

Tags:

php

mysql

I'm just trying to make operation which looks pretty simple but getting a strange error

Uncaught exception: 'PDOException'

Message: 'SQLSTATE[HY000]: General error: could not call class constructor'

Stack trace:

0 C:\xampp\htdocs\hotel\Core\Model.php(48): PDOStatement->fetch()
1 C:\xampp\htdocs\hotel\App\Controllers\Admin\Rooms.php(188): Core\Model::findById('98')***
2 C:\xampp\htdocs\hotel\Core\Router.php(78): App\Controllers\Admin\Rooms::deletePhoto()
3 C:\xampp\htdocs\hotel\public\index.php(62): Core\Router->dispatch('admin/rooms/del...')
4 {main}**

Thrown in 'C:\xampp\htdocs\hotel\Core\Model.php' on line 48

This is the function which causes this error.

 public static function findById($id){

        $sql = 'SELECT * FROM ' . static::$db_table . ' WHERE id = :id';
        $db = static::getDB();
        $statement = $db->prepare($sql);
        $statement->bindParam(':id', $id, PDO::PARAM_INT);
        $statement->setFetchMode(PDO::FETCH_CLASS, get_called_class());

        $statement->execute();

        return $statement->fetch();
    }

I already checked composer.json psr-4 section and it looks like all directories with my classes are there. So why can I get this error?

And here is the whole class with that method

namespace Core;

use PDO;
use App\Config;
abstract class Model {


protected static function getDB()
{
    static $db = null;

    if ($db === null) {

        $dsn = 'mysql:host=' . Config::DB_HOST . ';dbname=' .
            Config::DB_NAME . ';charset=utf8';
        $db = new PDO($dsn, Config::DB_USER, Config::DB_PASSWORD);

        // Throw an exception when an error occurs
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    }
    return $db;
}

// finds everything by ID (photo, user, booking)
public static function findById($id){

    $sql = 'SELECT * FROM ' . static::$db_table . ' WHERE id = :id';
    $db = static::getDB();
    $statement = $db->prepare($sql);
    $statement->bindParam(':id', $id, PDO::PARAM_STR);
    $statement->setFetchMode(PDO::FETCH_CLASS, '\App\Models\Admin\Photo');

    $statement->execute();

    return $statement->fetch();
}

// this method deletes via id
public static function delete($id){

    $sql = 'DELETE FROM ' . static::$db_table . ' WHERE ' . static::$column .  ' = :id';

    $db = static::getDB();
    $statament = $db->prepare($sql);

    $statament->bindValue(':id', $id, PDO::PARAM_STR);
    return $statament->execute();
}

// returns everything from selected database table
public static function findAll(){

    $sql = 'SELECT * FROM ' . static::$db_table;

    $db  = static::getDB();
    $stm = $db->prepare($sql);
    $stm->setFetchMode(PDO::FETCH_CLASS, get_called_class());

    $stm->execute();

    return $stm->fetchAll();
}



}

And here my Photo class: ` namespace App\Models\Admin;

use PDO;
use \App\Config;


class Photo extends \Core\Model {

public $errors_on_upload = [];                          // array for saving error messages
public static $db_table = 'photos';                     // database table
public static $column = 'id';
protected static $upload_derictory = 'public\uploads\pictures\rooms'; // path to uploaded pictures
protected static $path = '/uploads/pictures/rooms';
protected static $path_to_unlink = 'uploads/pictures/rooms/';
public $upload_errors_array = array(


    UPLOAD_ERR_OK            => "There is no errors",
    UPLOAD_ERR_INI_SIZE      => "The uploaded file exceeds max size",
    UPLOAD_ERR_FORM_SIZE     => "The uploaded file exceeds max size of form post request",
    UPLOAD_ERR_PARTIAL       => "The uploaded file was only partially uploaded",
    UPLOAD_ERR_NO_FILE       => "No file was uploaed",
    UPLOAD_ERR_NO_TMP_DIR    => "Missing the temporary folder",
    UPLOAD_ERR_CANT_WRITE    => "Failed to write to disk",
    UPLOAD_ERR_EXTENSION     => "A php extension stopped the file upload",

);


// supplied with $_FILES and input name of multiply input file
public function __construct($picture) {

        // here we creates properties and their values out of keys and values
        foreach($picture as $key => $value){
            $this->$key = $value;
            //echo $key;
        }

}

// creates a easy-readable array of properties and their values out of multiply-files array from form
// must be supplied with argument in format $_FILES['input_name']
public static function reArrayFiles($file_post) {

    $file_ary = array();
    $file_count = count($file_post['name']);
    $file_keys = array_keys($file_post);

    for ($i=0; $i < $file_count; $i++){
        foreach ($file_keys as $key){
            $file_ary[$i][$key] = $file_post[$key][$i];
        }
    }

    return $file_ary;
}

// this function saves photos to the database
public function save($room_id, $i){

    // first validate uploaded file
    $this->validatePhoto();

    if(empty($this->errors_on_upload)){ // insert data only if array with errors is empty

        $this->filename = time() . $this->name;

        $sql = 'INSERT INTO ' . static::$db_table . ' (room_id, main, name, type, size, path) VALUES (:room_id, :main, :name, :type, :size, :path)';
        $db  = static::getDB();

        $stm = $db->prepare($sql);

        $i = ($i === 0) ? true : false;

        $stm->bindValue(':room_id', $room_id, PDO::PARAM_INT);
        $stm->bindValue(':main', $i, PDO::PARAM_INT);
        $stm->bindValue(':name', $this->filename, PDO::PARAM_STR);
        $stm->bindValue(':type', $this->type, PDO::PARAM_STR);
        $stm->bindValue(':size', $this->size, PDO::PARAM_INT);
        $stm->bindValue(':path', $this->pathToPicture(), PDO::PARAM_STR);

        $stm->execute();

        $target_path = dirname(__DIR__, 3) . Config::DS . static::$upload_derictory . Config::DS . $this->filename;
        if(file_exists($target_path)){
            $this->errors_on_upload[] = 'This file already exists in this directory';
            return false;
        }

        if( !empty($this->tmp_name)){ // if tmp_name is empty, we just don't upload files

            if( ! move_uploaded_file($this->tmp_name, $target_path)){
                $this->errors_on_upload[] = 'The folder probably doesnt have permissions';
            } else {
                return true;
            }

        }

    }
    return false; // on failure
}



// this method validates pictures on upload
protected function validatePhoto(){

    $extension = $this->type;

    if( !empty($extension)){
        if($extension != 'image/jpeg' && $extension != 'image/png' && $extension != 'image/jpg'){
            $this->errors_on_upload[] = "Your file should be .jpeg, .jpg or .png";
        }
    }

    if($this->size > Config::MAX_FILE_SIZE){
        $this->errors_on_upload[] = "Your picture shouldn't be more than 10 Mb";
    }

    if($this->error != 0 && $this->error != 4) { //0 means no error, so if otherwise, display a respective message, 4 no files to upload, we allow that
        $this->errors_on_upload[] = $this->upload_errors_array[$this->error];
    }

}



protected function pathToPicture(){
    return static::$path . Config::DS . $this->filename;
}




public static function findAllPhotosToONeRoom($room_id, $main_photo_only){

    $sql  = 'SELECT * FROM photos WHERE room_id = :id'; // we need to pass only the main picture to all_rooms template
    $sql .= $main_photo_only ? ' AND main = 1' : '';    // get only main photo or all

    $db  = static::getDB();

    $stm = $db->prepare($sql);

    $stm->bindValue(':id', $room_id, PDO::PARAM_INT);



    if(($stm->execute())){


        if($main_photo_only){        // fetch all or only one row (for all_rooms page or for a particular room page)

            $pictures = $stm->fetch();

            return $pictures;

        } else {
            $pictures = $stm->fetchAll();

            return $pictures;
        }



    } else {
        // return a sample array with placeholder on failure instead of false to avoid further use 'false' as an array
        return false;
    }


}

// sets photo as a main by id and unsets prev main one
public static function setPictureAsMain($picture_id, $room_id){

    // check whether id's were supplied by AJAX request
    if($picture_id !== null && $room_id !== null){

        // first set old main photo main column to 0
        if(static::unsetMainPhoto($room_id)){

            // set chosen photo as main
            $sql = 'UPDATE ' . static::$db_table . ' SET main = 1 WHERE id = :picture_id';
            $db  = static::getDB();

            $stm = $db->prepare($sql);
            $stm->bindValue(':picture_id', $picture_id, PDO::PARAM_INT);

            return $stm->execute();

        }

    }

     return false;

}

// unsets main photo of a room
public static function unsetMainPhoto($room_id){

    $sql = 'UPDATE ' . static::$db_table . ' SET main = 0 WHERE room_id = :room_id AND main = 1';
    $db  = static::getDB();

    $stm = $db->prepare($sql);
    $stm->bindValue(':room_id', $room_id, PDO::PARAM_INT);

    return $stm->execute();
}


// this method adds photos to an existing room
public static function addPhotos($room_id, $pictures){

    // array for errors
    $errors_on_update = array();

    foreach ($pictures as $picture){

       if($picture->save($room_id, true)){

           $errors_on_update[] = true;

       }

    }

    return in_array(0, $errors_on_update, false) ? false : true;

}


// this method deletes images from upload folder
public static function unlinkImages($filename){
    return unlink(static::$path_to_unlink . $filename);
}



}`
like image 690
turisap Avatar asked Jul 31 '26 12:07

turisap


2 Answers

Not a direct response to the OP's specific issue, but General error: could not call class constructor could also mean that while the specified class constructor call was successful, the values from the database do not match the class-defined properties types.

For example, trying to use PDO::FETCH_CLASS with the following class:

class Something {
  public string $foo;
}

Would return the above error if the value of the foo column would happen to be null for any returned rows.

To find the real problem, one needs in look at the PDOException's getPrevious(), which would contain a TypeError with the message Cannot assign null to property Something::$foo of type string, for the above example.

like image 200
Guillaume Boudreau Avatar answered Aug 02 '26 05:08

Guillaume Boudreau


As mentioned in the documentation:

public array PDOStatement::fetchAll (
    int $fetch_style,
    mixed $fetch_argument,
    array $ctor_args = array()
)

ctor_args

Arguments of custom class constructor when the fetch_style parameter is PDO::FETCH_CLASS.

the same argument is available for setFetchMode

public bool PDOStatement::setFetchMode (
    int $PDO::FETCH_CLASS,
    string $classname,
    array $ctorargs
)

I would implement this accordingly:

First add a property $ctorArgs to \Core\Model with a default value of null. Followed by replacing the current setFetchMode calls

if (!is_array($this->ctorArgs)) {
    throw new \Excpeption("\"ctorArgs\" must be of type array");
}
$stm->setFetchMode(PDO::FETCH_CLASS, get_called_class(), $this->ctorArgs);

And finally add a property $ctorArgs to \App\Models\Admin\Photo with a default value of array("picture").

This will tell the code that your constructor is expecting the parameter $picture.

I would think about using a constant instead of a property, but that depends on how you decide to implement this.

like image 44
Dymen1 Avatar answered Aug 02 '26 05:08

Dymen1



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!