Troubleshooting Class Methods

I just added a method to my Database class, and now my code does not work. What is the best way for me to debug this method? Note: I work on my own, so do not have access to all the resources and tools that large agencies have so I would like to use the most straightforward, simple way possible to debug this (eg I have not idea how to use unit testing or exactly what that means, if that is relevant).

Part of my problem is that I am still learning PHP OOP, and each tutorial does something in a different way. So when I come across something in one tutorial that I would like to integrate into my classes, because it seems like it would be useful, I often get into trouble trying to add it even with modifications that seem to be needed.

BTW, I won’t add my code just yet, because I want to try to debug this myself. I’m just looking for strategies here. Thanks. :smile:

What tools (if any) are you using? Are you using an IDE? If so, which one? The most basic way is adding echo/die statements to see how far in the code you are getting and to dump variables, but if you are using IDE, you may want to spend a few minutes getting xDebug setup as that would make things a lot easier.

I’d also turn on display_errors in the PHP ini (so long as it isn’t a public site on a public server)

I am working locally and I keep my display_errors turned on all the time. I also have this at the top of my init.php file which is included in all my pages:

ini_set("error_reporting", "true");
error_reporting(E_ALL & E_NOTICE & E_STRICT & E_DEPRECATED);

My first line of defence is always echoing out statements and dumping variables, but nothing shows up here. Also my class works fine if I comment out my new method.

I will be on the road for a bit, but when I get back to work, I will try everything again and see if I can give you more info.

I’m getting the white screen, so I’m kind of working in the dark here.

Okay, there is also an Apache error log that may provide more detail. I definitely get the frustration with white screen errors. Most of the times it is related to bad file include. Since you recently changes your database, I’d look to see how that information is getting included.

I would check into Dependency Injection here

That way once you do get a class or classes to work then you can do unit testing and not worrying which class is working or not.

Here’s a small example of what I’m talking about:

<?php
class Author {
    private $firstName;
    private $lastName;
     
    public function __construct($firstName, $lastName) {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
 
    public function getFirstName() {
        return $this->firstName;
    }
 
    public function getLastName() {
        return $this->lastName;
    }
}
 
class Question {
    private $author = NULL;
    private $question;
 
    public function __construct($question, Author $author = NULL) {
        $this->author = $author;
        $this->question = $question;
    }
 
    public function getAuthor() {
        return $this->author;
    }
 
    public function getQuestion() {
        return $this->question;
    }
}
$trivia = new Question('Who wrote "The Stand"?', new Author('Stephen', 'King'));
$author = $trivia->getAuthor();

echo $trivia->getQuestion() . "<br>\n";
echo $author->getFirstName() . ' ' . $author->getLastName() . "<br>\n"; 
echo $trivia->getAuthor()->getFirstName() . ' ' . $trivia->getAuthor()->getLastName() . "<br>\n";

It’s a silly example but I could just test the Author Class out if I wanted to or even Question Class out separately.

But I don’t know how to do unit testing, @Pepster. And I have such a huge list of “things-to-learn” that I can’t add that to my list right now. That’s why I’m doing my debugging the inefficient way. But it’s a personal project and not huge.

BTW, I just found the error that made my new method kill the class when I reactivated the method and tried uncommenting its contents line by line.

But why did my page not display any errors?

Can you share that code (with the error)? I’d love to try it on a few setups I have to see if maybe it is a PHP setup issue.

Here’s my database class:

<?php

    class Database {
        
        private $host = DB_HOST;
        private    $user = DB_USER;
        private $pass = DB_PASS;
        private $dbname = DB_NAME;
        
        private $dbh;
        private $error;
        private $stmt;
        
        /*
        *    Constructor function
        */
        public function __construct() {
            
            // Set DSN
            $dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
            
            // Set options
            $options = array (
                PDO::ATTR_PERSISTENT => true,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            );
            
            // Create a new PDO instance
            try {
                $this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
            } catch (PDOException $e) {
                $this->error = $e->getMessage();
            }
            
        }
        
        public function query($query) {
        
            $this->stmt = $this->dbh->prepare($query);
            
        }
        
        public function bind($param, $value, $type=null) {
            
            if (is_null($type)) {
                switch (true) {
                    case is_int($value) :
                        $type = PDO::PARAM_INT;
                        break;
                    case is_bool($value) :
                        $type = PDO::PARAM_BOOL;
                        break;
                    case is_null($value) :
                        $type = PDO::PARAM_NULL;
                        break;
                    default :
                        $type = PDO::PARAM_STR;
                        break;        
                }
            }
            $this->stmt->bindvalue($param, $value, $type);
            
        }
        
        public function execute() {
            
            return $this->stmt->execute();
            
        }
        
        /*
        *    Get result set of a db query (more than one record)
        */
        public function getResultSet() {
            
            $this->execute();
            return $this->stmt->fetchAll(PDO::FETCH_OBJ);
            
        }
        
        /*
        *    Get single row from a db query 
        */
        public function getSingle() {
            
            $this->execute();
            return $this->stmt->fetch(PDO::FETCH_OBJ);
            
        }
        
        public function getRowCount() {
        
            return $this->stmt->rowCount();
            
        }
        
        public function getLastInsertId() {
            
            return $this->dbh->lastInsertId();
            
        }
        
        public function insertEntry($table, $data, $insert_values) {
        
            $stmt = $this->query("INSERT INTO $table ($data)
                                VALUES ($insert_values)");
            $this->execute();
            return $this->getLastInsertId();
            
        }
    
        public function beginTransaction() {
            
            return $this->dbh->beginTransaction();
            
        }
        
        public function endTransaction() {
            
            return $this->dbh->commit();
            
        }
        
        public function cancelTransaction() {
            
            return $this->dbh->rollBack();
            
        }
     
    }

Here is the code I am trying to run:

<?php
require_once '../config/init.php';

require_once '../libraries/database.class.php';

$db = new Database();

try {
    $stmt = $db->query("SELECT id, contact_person, contact_position, phone1, phone2, email1, email2 FROM clients");
    $results = $db->getResultSet();
} catch (Exception $e) {
    displayErrorPage($e->getMessage());
}

foreach ($results as $row) {
        
    $id= $row->id;
    $contact_person = $row->contact_person;
    $contact_position = $row->contact_position; 
    $phone1 = $row->phone1;
    $phone2 = $row->phone2;
    $email1 = $row->email1;
    $email2 = $row->email2;
    
    $data = array('client_id', 'name', 'position', 'phone1', 'phone2', 'email1', 'email2');
    $insert_values = array($id, $contact_person, $contact_position, $phone1, $phone2, $email1, $email2);
    $table = "contacts";
    
    $db->insertEntry($table, $data, $insert_values);
    
}

This is not part of my application, it is just my attempt at automating the transfer of some of the fields of one database table into a new table, because I decided to change the structure of my database. After the transfer, I was going to drop the fields in the first table (clients) that corresponded to the ones in the new table (contacts).

I want to put all my CRUD operations in the database class, instead of having them in each of the other classes such as Client, Project, etc and just specify the fields, data, and table name in the file that calls the method.

The method in question is insertEntry(). It turns out it was missing the final ‘)’ in the query line if you want to duplicate the error.

But even after fixing that syntax error, so that I can get my page to echo things, I still can’t get that method to work insert into my database. I am not using prepared statements here, because this is a personal project, but maybe I should add that here too.

The above right? WIth the ) right after $insert_values was missing?

Just want to make sure when I get a chance, I’ve got the right location :smile:

It was the ‘)’ just before the ‘;’ that was missing.

BTW, I just made one small correction and concatenated the $table variable in the query. Should I be doing that with the arrays $data and $insert_values?

Yes and No. Technically they are arrays, so you need to run them through implode and comma delimit them. However, you still need to also add quotes to the values when they are strings, etc. It may be better to utilize a prepared statement for this, as prepared statements let you bind an array.

So the result I want to end up with is this:

$insert_values = array(
    ':id' => $row->id, 
    ':contact_person' => $row->contact_person, 
    ':contact_position' => $row->contact_position, 
    ':phone1' => $row->phone1, 
    ':phone2' => $row->phone2, 
    ':email1' => $row->email11, 
    ':email2' => $row->email2
);

$query = "INSERT INTO contacts ( client_id, name, position, phone1, phone2, email1, email2) 
                VALUES ( :client_id, :name, :position, :phone1, :phone2, :email1, :email2)
                ";
$stmt = $db->prepare($query);

$stmt->execute($insert_values);

and I am using this:

foreach ($results as $row) {
        
    $field_names = array('client_id', 'name', 'position', 'phone1', 'phone2', 'email1', 'email2');
    $insert_values = array(
        ':id' => $row->id, 
        ':contact_person' => $row->contact_person, 
        ':contact_position' => $row->contact_position, 
        ':phone1' => $row->phone1, 
        ':phone2' => $row->phone2, 
        ':email1' => $row->email1, 
        ':email2' => $row->email2
    );
    
    $table_name = "contacts";
    
    $db->insertEntry($table, $field_names, $insert_values);
    
}

and this method in the Database class:

public function insertEntry($table_name, $field_names, $insert_values) {
        
            $stmt = $this->query("INSERT INTO " . `$table_name` .  "($field_names)
                                VALUES ($insert_values)");
            $this->execute($insert_values);
            return $this->getLastInsertId();
            
        }

where the methods query() and execute() and getLastInsertId() are defined elsewhere in the Database class.

I’m still not sure exactly how to deal with the $field_names, and $insert_values arrays (I changed their names).

“so you need to run them through implode and comma delimit them”

I thought my arrays were already comma-delimited.

Okay, take insertEntry and change the internal implementation to

public function insertEntry($table_name, $field_names, $insert_values) {
    $query = "INSERT INTO {$table_name} ( " . implode(",", $field_names) . ") VALUES ( " . implode(",", array_keys($insert_values)) . ")";
    $stmt = $this->dbh->prepare($query);
    $stmt->execute($insert_values);
    return $this->getLastInsertId();
}

I think that would get you closer to your goal.

That worked like a charm. Thank you!!

I like the way you saved effort by using the keys of the array $insert_values in the VALUES. I would have just formed another array, and things would have started getting out of hand.

One question: I thought the arrays $field_names and $insert_values were already comma delimited. Why did you need to use implode here?

1 Like

Unless PHP has changed something (and maybe it has), arrays are not strings, and you need to treat the array as a string (since you want to embed it into a query).

Case in point with this simple script

$myarray = array('client_id', 'name', 'position', 'phone1', 'phone2', 'email1', 'email2');
echo $myarray;
echo " versus ";
echo implode(",", $myarray);

which outputs:

Array versus client_id,name,position,phone1,phone2,email1,email2

No it hasn’t changed anything. I just don’t consciously think of the fact that an array is not a string and that a query is a string, even though I really did know that - just not enough experience trying to merge arrays with queries. Thanks for explaining it in such a clear way!

Any time. I still plan to run your initial error through a few dev setups I have, but I probably won’t get to it in the next hour or two like I had hoped, it may be later.

Okay, in the Vagrant setup I just put together, I got the following error when I removed that closing parenthesis

Parse error: syntax error, unexpected ';' in /var/www/sitepoint-demos/webmachine/class-methods/database.class.php on line 106

The Vagrant machine has display_errors off and has error_reporting set to “E_ALL & ~E_DEPRECATED & ~E_STRICT” … so I’m not sure why yours showed a blank white page.

I’m not sure either. But I’m glad the issue was solved.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.