PHPUnit Mocking functions which use Database Connection - php

I am new to using PHPUnit and i am trying to test function getAllTasks() which need to fetch all tasks from database. I tried everything but i am just making my code worse. So please help me to solve the problem. TaskTest.php is something i tried to make test but it dont works. And sure if there are better ways to do something, i like to learn new stuff too. Here is my code:
EDIT: I changed code for TaskTest.php and i managed to get test pass. Can someone please tell me if this is good way to test this function, or there are better ways? Thanks!
Task.php
<?php
require_once 'Database.php';
class Task {
private $db;
public function __construct() {
$this->db = new Database;
}
public function getAllTasks() {
$this->db->query('SELECT * FROM tasks');
$results = $this->db->resultSet();
return $results;
}
}
Database.php
<?php
class Database {
private $host = 'localhost';
private $user = 'root';
private $pass = '123456';
private $dbname = 'todolist';
private $dbh;
private $stmt;
private $error;
public function __construct(){
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create PDO instance
try {
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
} catch(PDOException $e){
$this->error = $e->getMessage();
echo $this->error;
}
}
public function query($sql){
$this->stmt = $this->dbh->prepare($sql);
$this->execute();
}
public function execute(){
return $this->stmt->execute();
}
public function resultSet(){
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
TaskTest.php
<?php
require_once './src/Task.php';
require_once './src/Database.php';
use PHPUnit\Framework\TestCase;
class TaskTest extends TestCase {
public function testGetAllTasks() {
$table = array(
array(
'task_id' => '1',
'task_desc' => 'Task One Test'
),
array(
'task_id' => '2',
'task_desc' => 'Task Two Test'
)
);
$dbase = $this->getMockBuilder('Database')
->getMock();
$dbase->method('resultSet')
->will($this->returnValue($table));
$expectedResult = [
'task_id' => '1',
'task_desc' => 'Task One Test',
];
$task = new Task();
$actualResult = $task->getAllTasks();
$this->assertEquals($expectedResult, $actualResult[0]);
}
}

You pass the mock to the Task class constructor, but it doesn't do anything with it.
$task = new Task($resultSetMock);
Updated the code so that it will be used:
class Task {
private $db;
public function __construct( ?Database $db = null ) {
// set the db if none is provided
if( is_null($db) )
{
$db = new Database;
}
$this->db = $db;
}
// ...
}

Related

I'm currently making a profile page for my system, what is the correct syntax for this?

<?php
$conn = mysql_connect("localhost","root","");
mysql_select_db("db",$conn);
$result=mysql_query("SELECT * FROM users where empID");
while($test = mysql_fetch_array($result))
{
$id = $test['empID'];
echo "<p>".$test['fname']."</p>";
}
?>
I don't know what to do. I'm new to this. Thanks!
mysqli is dreprecied use PDO:
<?php
#backslash is important to spécified you want a native class
use \PDO;
class Connexion{
private static $instance;
private $server_setting = [];
private $database;
#use singleton to instanciate
private function __construct($file){
$this->file = require($file);
$this->connect();
}
private function get($key){
return (isset($this->file[$key])) ? $this->file[$key] : new \Exception('Unknow server identifiant');
}
public function connect(){
try{
$database = new PDO('mysql:dbname='.$this->get('db_name').';host='.$this->get('db_host'),
$this->get('db_user'),
$this->get('db_pass'),
array(PDO::ATTR_PERSISTENT => TRUE,
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING));
#For accent like : éêèïçõ...
$database->exec("SET CHARACTER SET utf8");
}catch (Exception $e)
{
die('Erreur : ' . $e->getMessage());
}
$this->database = $database;
}
protected function sinInstance($file){
return (self::$instance == NULL) ? self::$instance = new MySQL($file) : self::$instance;
}
protected function getDatabase(){
return $this->database
}
}
?>
class for making request:
<?php
use Connexion;
class Statement extends Connexion{
private $pdo;
public function __construct($file){
$this->pdo = parent::sinInstance($file)->getDatabase();
return $this;
}
public function mine(){
return $this->pdo;
}
#You can also put general function here like:
public function creat(){}
public function select(){}
public function update(){}
public function delete(){}
#thinking about entity and function get_object_vars();
}
?>
The config file:
<?php
# File to config.
return array(
"db_name" => "mydb",
"db_host" => "localhost",
"db_user" => "root",
"db_pass" => ""
);
?>
index.php :
<?php
const SERVER_SETTING = 'root/to/my/server-setting/file'
use Statement;
$pdo = new Statement(SERVER_SETTING)->mine();
$statement = $pdo->prepare('SELECT * FROM mytab');
$statement->execute();
$statement->fecthAll();
#or
$pdo = new Statement(SERVER_SETTING);
$statement = $pdo->mine->prepare('SELECT * FROM mytab');
$statement->execute();
#for entity: statement->setFetchMode();
$statement->fetchAll();
?>
Good luck

Passing PDO object into class - PHP Fatal error: Call to a member function execute() on a non-object

I am refactoring a (procedural) PHP Library I wrote a while back into a lightweight OOP framework. I'm getting caught up with trying to pass a PDO object to be used in a class. Here's what I've got so far.
Config.php
<?php
class Config {
// Database Variables
private $db_type;
private $db_host;
private $db_user;
private $db_pass;
private $db_name;
private $db_path; // for sqlite database path
private $db_char; // charset
// Site Variables
private $s_protocol;
private $s_subdomain;
private $s_domain;
private $s_tld;
private $s_dir;
private $s_name;
private $s_description;
private $s_path;
private $s_visibility;
private $s_pipe;
private $s_apps;
private $s_hooks;
private $s_blocks;
private $s_assets;
// User Default
private $u_groupid;
public function __construct($config) {
$this->set($config);
}
public function set($config) {
if (!empty($config) && is_array($config)) {
foreach ($config as $k => $v) {
if (property_exists(get_class($this), $k)) {
$this->$k = $v;
}
}
return true;
}
else { return false; }
}
public function get($config) {
if (!empty($config)) {
return $this->$config;
}
}
public function domain() {
return $this->get('s_protocol') .'://'. $this->get('s_domain') . $this->get('s_tld') .'/'. $this->get('s_dir');
}
}
?>
Database.php
<?php
class Database extends PDO {
private $config;
public function __construct($config) {
$this->config = $config;
switch($this->config->get('db_type')) {
case 'mysql':
case 'pgsql':
try {
return new PDO(
$this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
}
catch(PDOException $e) {
die($e->getMessage());
}
break;
case 'sqlite':
try {
return new PDO($this->config->get('db_type') .':'. $this->config->get('db_path'));
}
catch(PDOException $e) {
die($e->getMessage());
}
break;
case 'firebird':
try {
return new PDO(
$this->config->get('db_type') .':dbname='. $this->config->get('db_host') .':'. $this->config->get('db_path'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
}
catch(PDOException $e) {
die($e->getMessage());
}
break;
case 'informix':
try {
return new PDO(
$this->config->get('db_type') .':DSN='. $this->config->get('db_name'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
}
catch(PDOException $e) {
die($e->getMessage());
}
break;
case 'oracle':
try {
return new PDO(
'OCI:dbname='. $this->config->get('db_name') .';charset='. $this->config->get('db_char'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
}
catch(PDOException $e) {
die($e->getMessage());
}
break;
}
}
}
?>
Auth.php
<?php
class Auth {
// Set Database object
protected $db;
// User fields in users table
private $id;
private $email;
private $password;
private $firstname;
private $lastname;
private $displayname;
private $groupid;
private $ip;
private $created;
private $updated;
private $cookie;
private $sessionid;
private $lastlogin;
private $token;
private $active;
public function __construct($dbh) {
$this->db = $dbh;
}
public function add($params) {
$sql = '
INSERT INTO
`users` (
';
$cols = array_keys($params);
$col_string = implode(', ', $cols);
$sql .= $col_string .'
)
VALUES (
';
array_walk($cols, function(&$v, $k) { $v = ':'. $v; });
$col_string = implode(', ', $cols);
$sql .= $col_string .'
)
';
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
}
public function remove($params) {
}
public function update($params) {
}
public function get($params) {
}
protected function set($params) {
if (!empty($params) && is_array($params)) {
foreach ($params as $k => $v) {
if (property_exists(get_class($this), $k)) {
$this->$k = $v;
}
}
return true;
}
else { return false; }
}
}
?>
init.php
<?php
session_start();
$params = array(
'db_type' => 'mysql',
'db_host' => '127.0.0.1',
'db_user' => 'user',
'db_pass' => 'password',
'db_name' => 'database',
'u_groupid' => 4
);
require_once('Config.php'); $c = new Config($params);
require_once('Database.php'); $db = new Database($c);
require_once('Auth.php'); $u = new Auth($db);
$user = array(
'email' => 'newperson#email.com',
'password' => md5('password'),
'firstname' => 'Jeff',
'lastname' => 'Wilson',
'displayname' => 'Jeff Wilson',
'groupid' => $c->get('u_groupid'),
'ip' => $_SERVER['REMOTE_ADDR'],
'created' => date('Y-m-d H:i:s'),
'sessionid' => session_id(),
'active' => 1,
);
$u->add($user);
?>
PHP Fatal error: Call to a member function execute() on a non-object in Auth.php on line 46
This is line 46:
$stmt->execute($params);
As far as I know I'm passing the PDO object to the Auth class correctly. It shouldn't say it's a non-object. Can anyone else see what's wrong here?
The constructor in the Database class is returning a value (a PDO object). The __construct() function should not explicitly return a value. Since your Database class extends PDO, call the parent constructor instead:
parent::__construct(
$this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
Unless the pdo instance is set explicitly to use exceptions for error reporting you have to check the return value of PDO::prepare
$stmt = $this->db->prepare($sql);
if ( !$stmt ) {
// prepare failed
// the array returned by $this->db->errorinfo() most likely contains more info about the error
// don't send it unconditionally,directly to the browser, see https://www.owasp.org/index.php/Top_10_2013-A6-Sensitive_Data_Exposure
}
else {
$result = $stmt->execute($params);
if ( !$result ) {
// not ok
}
else {
// ok
}
}
edit: I can leave out the first part of my answer, since Alex Ivey already wrote it. ;-)
Just image behind the scene php is doing something like
function __internal_newDatabase() {
// just image the parser/compiler creates this function
// from your class defintion
$instance = array(
'properties'=>array('config'=>null),
'methods'=>array('beginTransaction'=>PDO::beginTransaction, 'prepare'=>PDO::prepare, ...)
);
// some magic function that calls a method and "replaces" $this by the second parameter
invoke(Database::__construct, $instance);
return $instance;
}
and when your script contains new Database instead it calls __internal_newDatabase(). That's (ooooversimplified) what happens and therefore you can not just "change" the instance within your constructor "method" by returning a different instance. Your constructor is supposed to make this instance fly (or bail out by throwing an exception).
Your class Database is derived from PDO, i.e. it is supposed to behave as a PDO. In other languages that implies that the/a base class' constructor must be called. PHP doesn't enforce that. But in this case leaves your Database instance unusable. You have to explcitily call the parent's constructor as Alex's answer shows.
But there are other issues with this class. (First a confession: I'm biased as can be against class Database. In virtually all in all cases it's just wrong and therfore automagically raises a red flag for me)
Foremost: Given its name and the way you use it, it's superfluous. It's just a configuration detail not a class derived from PDO. More likely it would be a factory and/or something within an IoC container (if you use that).
On the other hand it might not be just a configuration detail but might (and probably will) cause different implementions for different databases. PDO is not a database abstraction, just a unified access layer.
Your class Auth.php doesn't care about the concrete sql dialect used - and this particular query will most likely work for all database systems supported by PDO. But there will sooner or later be queries that have to be customized for the different RDBMSs. And then your base class would probably be called something like DB_Adapter and there would be MySQL_Adapter extends DB_Adapter and so on....
Add this method to your Database class
public function getConnection() {
return new PDO(
$this->config->get('db_type') .':dbname='. $this->config->get('db_name') .';host='. $this->config->get('db_host'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
}
call prepare statement like this:
$conn = $this->db->getConnection();
$stmt = $conn->prepare($sql);
Your $conn has to be PDO object in this case
The main problem here is while $db = new Database($c); seems to be fine at the first glance, calling $db->prepare is not good because $db is instance of Database, but has to be instance of PDO
One of the ways to improve a bit connection handling would be:
In your Database class to have a private $conn for connection
class Database extends PDO {
private $config;
private $conn;
public function __construct($config) {
$this->config = $config;
switch($this->config->get('db_type')) {
case 'mysql':
case 'pgsql':
try {
$this->conn = new PDO(
$this->config->get('db_type') . ':dbname=' . $this->config->get('db_name') . ';host=' . $this->config->get('db_host'),
$this->config->get('db_user'),
$this->config->get('db_pass')
);
} catch(PDOException $e) {
die($e->getMessage());
}
break;
// ...
}
}
THEN in the same class new method to return connection:
public function getConnection() {
return $this->conn;
}
AND FINALLY call it:
$this->db->getConnection()->prepare($sql)

PDO wont connect to the database

I've made a simple Database class to handle my database connections. But it's somehow not working? At first it wasn't working with MySQLi, so i tried PDO – which ain't working either.
I am however eager to make PDO work. I've already googled and searched here at StackOverflow, but without luck.
Here's my class:
class Database
{
// Local
protected $_host = "localhost";
protected $_user = "root";
protected $_pass = "root";
protected $_database = "hs";
protected $_connection;
// Construct
private function __construct()
{
try
{
$this->_connection = new PDO('mysql:host=localhost;dbname=hs', $this->_user, $this->_pass);
$this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo 'ERROR: ' . $e->getMessage();
}
}
public function login($usr, $pwd)
{
echo "hi";
}
}
And here's the execution:
if(isset($_POST['hs_login']))
{
$db = new Database;
$db->login($_POST['hs_username'], $_POST['hs_password']);
}
Thanks in advance! :)
Constructors are always public so change that like so:
class Database
{
// Local
protected $_host = "localhost";
protected $_user = "root";
protected $_pass = "root";
protected $_database = "hs";
protected $_connection;
// Construct
public function __construct()
{
try
{
$this->_connection = new PDO('mysql:host=localhost;dbname=hs', $this->_user, $this->_pass);
$this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo 'ERROR: ' . $e->getMessage();
}
}
public function login($usr, $pwd)
{
echo "hi";
}
}
Also, new Database is a method call so change that like so:
if(isset($_POST['hs_login']))
{
$db = new Database;
$db->login($_POST['hs_username'], $_POST['hs_password']);
}
Just wanted to point out, that there are some cases, when you can use private constructors. One of the practical use of them is with Databases, so it's relevant in your case as well. This design pattern is called Singleton pattern, and it relies on static method calls. You don't have to instantiate the class, as instantiation is handled by the class itself. I've put together an example:
<?php
class Database {
private static $instance = null;
private $db;
private static $last_result;
private function __construct() {
try {
$pdo_param = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
);
$this->db = new PDO("mysql:host=YOUR_HOSTNAME;dbname=YOUR_DBNAME", "YOUR_USERNAME", "YOUR_PASSWORD", $pdo_param);
}
catch (PDOException $e) {
die($e->getMessage());
}
}
private static function getInstance() {
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
public static function query($sql) {
try {
$instance = self::getInstance();
$stmt = $instance->db->prepare($sql);
$stmt->execute();
self::$last_result = $stmt->fetchAll();
return self::$last_result;
}
catch (PDOException $e) {
die($e->getMessage());
}
}
public static function prepare($sql, $params) {
try {
$instance = self::getInstance();
$stmt = $instance->db->prepare($sql);
$stmt->execute($params);
self::$last_result = $stmt->fetchAll();
return self::$last_result;
}
catch (PDOException $e) {
die($e->getMessage());
}
}
}
$users = Database::query("SELECT * FROM users");
$filtered_users = Database::prepare("SELECT * FROM users WHERE username = :username", array(":username" => "john_doe"));
?>
<pre><?php
print_r($users);
print_r($filtered_users);
?></pre>
Singleton design pattern is really useful when you want to make sure, that there's ONLY ONE instance of a class in any given time.

Objects with db connection

I have a:
class Person
{
public $data;
public function __construct($name) {
$database = new Database;
$database->prepare('SELECT * FROM persons where name = :name');
$database->bindParam(':name', $name);
$database->execute();
$this->data = $database->fetch();
}
}
and i´m creating persons like this:
$jim = new Person('Jim');
$mike = new Person('Mike');
and this is my database class
class Database
{
private $host = 'localhost';
private $user = 'test';
private $pass = '123';
private $dbname = 'test';
private $dbh;
private $error;
private $stmt;
public function __construct()
{
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try{
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
}
catch(PDOException $e){
$this->error = $e->getMessage();
echo $this->error;
}
}
public function prepare($query)
{
$this->stmt = $this->dbh->prepare($query);
}
public function bindParam($key, $val)
{
$this->stmt->bindParam($key, $val);
}
public function execute()
{
$this->stmt->execute();
}
public function fetch()
{
return $this->stmt->fetch(PDO::FETCH_ASSOC);
}
}
I´m getting this message very often
Warning: PDO::__construct(): MySQL server has gone away
I want to confirm if this approach is correct. calling the database in the constructor?
How can i know if the created person exists (i mean exist in the database)
should i do something like this?
$jim = new Person('Jim');
if($jim->data) {
echo 'person exists in the db';
}
thank you

Cannot access empty property php class

I'm trying to learn OOP, and some of its concept. I've following class for users:
class Users
{
private $host = DB_HOST;
private $user = DB_USERNAME;
private $pass = DB_PASSWORD;
private $dbname = DB_NAME;
private $conn;
private $stmt;
public $error;
function __construct()
{
$dsn = 'mysql:host='.$this->host.';dbname='.$this->dbname.';charset=utf8';
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
$this->conn = new PDO($dsn,$this->user,$this->pass,$options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
}
}
private function mysql_execute_query($sql,$params)
{
$this->stmt = $this->conn->prepare($sql);
$this->stmt->execute($params);
return $this->$stmt;
}
public function find_user_by_provider_uid($provider,$provider_uid)
{
$sql = 'SELECT * FROM users WHERE provider = :provider AND provider_uid = :provider_uid LIMIT 1';
$params = array(
':provider' => $provider,
':provider_uid' => $provider_uid
);
$result = $this->mysql_execute_query($sql,$params);
return $result->fetch();
}
}
First of all is there some tip that comes to mind for structuring this code better? or using more features of oop?
Second, it fails with following error:
PHP Notice: Undefined variable: stmt
PHP Fatal error: Cannot access empty property
Both of this lines refer to return $this->$stmt; inside mysql_execute_query
My hunch is that it has something to do with it being private function. But I cannot tell.
Any ideas?
Here the error:
return $this->$stmt;
But should be:
return $this->stmt;

Categories