I have a very weird behaviour with PDO. I won't go into much details as it would take up way too much time but basically what I observed is that when I re-use a \PDOStatement that performs a simple INSERT I sistematically get a wrong value when invoking PDO::lastInsertId().
The first time I execute the statement it works fine and I get back the right id. Subsequent executions will instead always return '0'. This is even more weird because it happens only between tests (PHPUnit ones). So say I execute the insert using the prepared statement in test1 (working), in test2 it will fail miserably.
When executing multiple times the prepared statement in a non unit-testing environment (in a simple php file fro instance) it all works fine and the last inserted ids are always accurate. Very weird indeed.
Here's the test (note that PersistencyManagerInstance is just a plain intsance of PersistencyManager):
<?php
class PersistencyManagerTest extends PHPUnit_Framework_TestCase {
const DELETE_ALL = "TRUNCATE user";
const ADD_USER = "INSERT INTO user values(null, :username, :password)";
const CHECK_USER_EXISTENCE = "SELECT * FROM user WHERE username = :username AND password = :password";
const DELETE_USER_BY_ID = "DELETE FROM user WHERE id = ?";
protected $manager = null;
public function __construct() {
$this->manager = new PersistencyManagerInstance(PDOFactory::build());
}
public function setUp() {
$this->manager->exec(self::DELETE_ALL);
}
public function tearDown() {
$this->manager->exec(self::DELETE_ALL);
}
public function testInsert() {
$user = new User("laurent", "password");
$id = $this->manager->insert(self::ADD_USER, $user->export());
$this->assertEquals("1", $id);
}
public function testInsertAgain() {
$user1 = new User("laurent1", "password1");
$id = $this->manager->insert(self::ADD_USER, $user1->export());
$this->assertEquals("1", $id);
}
public function testQuery() {
$user = new User("laurent", "password");
$this->manager->insert(self::ADD_USER, $user->export());
$results = $this->manager->query(self::CHECK_USER_EXISTENCE, $user->export());
$this->assertEquals(1, count($results));
}
public function testExec() {
$user = new User("laurent", "password---");
$id = $this->manager->insert(self::ADD_USER, $user->export());
$affected = $this->manager->exec(self::DELETE_USER_BY_ID, array($id));
$this->assertEquals(1, $affected);
}
}
testInsert works while testInsertAgain does not.
and here's the class:
<?php
namespace memory\manager;
use \PDO;
abstract class PersistencyManager {
/**
* #var array An array of \PDOStatement objects
*/
protected static $ps = array();
/**
* #var \PDO
*/
protected $connection = null;
protected function prepareStmt($sql) {
// return $this->connection->prepare($sql);
$key = md5($sql);
if (!isset(self::$ps[$key])) {
self::$ps[$key] = $this->connection->prepare($sql);
}
return self::$ps[$key];
}
public function __construct(PDO $connection) {
$this->connection = $connection;
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function __destruct() {
$this->connection = null;
}
/**
* Good for SELECT operations. By default it fetches using arrays.
* #param string $sql
* #param array $values
* #param integer $fetchStyle
* #return array A list of matching elements (The elements' type depends on $fetchStyle)
*/
public function query($sql, array $values = array(), $fetchStyle = PDO::FETCH_ASSOC) {
$prepared = $this->prepareStmt($sql);
$prepared->execute($values);
$prepared->setFetchMode($fetchStyle);
$all = $prepared->fetchAll();
$prepared->closeCursor();
return $all;
}
/**
* Good for INSERT operations.
* #param string $sql
* #param array $values
* #return string Last inserted element's id in string format
*/
public function insert($sql, array $values = array()) {
$prepared = $this->prepareStmt($sql);
$prepared->execute($values);
$prepared->closeCursor();
return $this->connection->lastInsertId();
}
/**
* Good for all the remaining routines.
* #param string $sql
* #param array $values
* #return integer The number of effected rows
*/
public function exec($sql, array $values = array()) {
$prepared = $this->prepareStmt($sql);
$prepared->execute($values);
$count = $prepared->rowCount();
$prepared->closeCursor();
return $count;
}
}
Any idea?
Cheers
guys I was starting a new connection at every test. That was the reason.
Related
I'm a beginner to PHP and I needed a login page with a member database so I found a script from a tutorial but I don't know what's wrong.
From phpMyAdmin I created a database called "admin_logs" and then created a table called "registered_users". But when I enter a user in to the table and then try to log in from the website it says that I entered invalid credentials. So it looks like I can't get the data to the site but I don't understand why:
index.php
<?php
session_start();
if(!empty($_SESSION["userId"])) {
require_once './view/dashboard.php';
} else {
require_once './view/login-form.php';
}
?>
login-action.php
<?php
namespace Phppot;
use \Phppot\Member;
if (! empty($_POST["login"])) {
session_start();
$username = filter_var($_POST["user_name"], FILTER_SANITIZE_STRING);
$password = filter_var($_POST["password"], FILTER_SANITIZE_STRING);
require_once (__DIR__ . "./class/Member.php");
$member = new Member();
$isLoggedIn = $member->processLogin($username, $password);
if (! $isLoggedIn) {
$_SESSION["errorMessage"] = "Invalid Credentials";
}
header("Location: ./index.php");
exit();
}
DataSource.php
<?php
namespace Phppot;
/**
* Generic datasource class for handling DB operations.
* Uses MySqli and PreparedStatements.
*
* #version 2.3
*/
class DataSource
{
// PHP 7.1.0 visibility modifiers are allowed for class constants.
// when using above 7.1.0, declare the below constants as private
const HOST = 'localhost';
const USERNAME = 'root';
const PASSWORD = '';
const DATABASENAME = 'admin_logs';
private $conn;
/**
* PHP implicitly takes care of cleanup for default connection types.
* So no need to worry about closing the connection.
*
* Singletons not required in PHP as there is no
* concept of shared memory.
* Every object lives only for a request.
*
* Keeping things simple and that works!
*/
function __construct()
{
$this->conn = $this->getConnection();
}
/**
* If connection object is needed use this method and get access to it.
* Otherwise, use the below methods for insert / update / etc.
*
* #return \mysqli
*/
public function getConnection()
{
$conn = new \mysqli(self::HOST, self::USERNAME, self::PASSWORD, self::DATABASENAME);
if (mysqli_connect_errno()) {
trigger_error("Problem with connecting to database.");
}
$conn->set_charset("utf8");
return $conn;
}
/**
* To get database results
* #param string $query
* #param string $paramType
* #param array $paramArray
* #return array
*/
public function select($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);
if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType, $paramArray);
}
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$resultset[] = $row;
}
}
if (! empty($resultset)) {
return $resultset;
}
}
/**
* To insert
* #param string $query
* #param string $paramType
* #param array $paramArray
* #return int
*/
public function insert($query, $paramType, $paramArray)
{
print $query;
$stmt = $this->conn->prepare($query);
$this->bindQueryParams($stmt, $paramType, $paramArray);
$stmt->execute();
$insertId = $stmt->insert_id;
return $insertId;
}
/**
* To execute query
* #param string $query
* #param string $paramType
* #param array $paramArray
*/
public function execute($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);
if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType="", $paramArray=array());
}
$stmt->execute();
}
/**
* 1. Prepares parameter binding
* 2. Bind prameters to the sql statement
* #param string $stmt
* #param string $paramType
* #param array $paramArray
*/
public function bindQueryParams($stmt, $paramType, $paramArray=array())
{
$paramValueReference[] = & $paramType;
for ($i = 0; $i < count($paramArray); $i ++) {
$paramValueReference[] = & $paramArray[$i];
}
call_user_func(array(
'bind_param'
), $paramValueReference);
}
/**
* To get database results
* #param string $query
* #param string $paramType
* #param array $paramArray
* #return array
*/
public function numRows($query, $paramType="", $paramArray=array())
{
$stmt = $this->conn->prepare($query);
if(!empty($paramType) && !empty($paramArray)) {
$this->bindQueryParams($stmt, $paramType, $paramArray);
}
$stmt->execute();
$stmt->store_result();
$recordCount = $stmt->num_rows;
return $recordCount;
}
}
member.php
<?php
namespace Phppot;
use \Phppot\DataSource;
class Member
{
private $dbConn;
private $ds;
function __construct()
{
require_once "DataSource.php";
$this->ds = new DataSource();
}
function getMemberById($memberId)
{
$query = "select * FROM registered_users WHERE id = ?";
$paramType = "i";
$paramArray = array($memberId);
$memberResult = $this->ds->select($query, $paramType, $paramArray);
return $memberResult;
}
public function processLogin($username, $password) {
$passwordHash = md5($password);
$query = "select * FROM registered_users WHERE user_name = ? AND password = ?";
$paramType = "ss";
$paramArray = array($username, $passwordHash);
$memberResult = $this->ds->select($query, $paramType, $paramArray);
if(!empty($memberResult)) {
$_SESSION["userId"] = $memberResult[0]["id"];
return true;
}
}
}
I'm assuming you've simply entered a user with a plaintext password into the database. (Don't ever store plain-text passwords in a database, for a million reasons past the problem at hand.) Now, note the following line in processLogin() in your member.php file:
$passwordHash = md5($password);
$query = "select * FROM registered_users WHERE user_name = ? AND password = ?";
In other words: Your login system is querying the database with username and md5(password), and if there's a match, the login is considered valid. This means that you will need to generate a md5-hash of the password, and enter that into the password field of your database.
That, I presume, is why your login fails.
I should note that MD5 isn't a particularly safe hashing method. Nor is SHA1 an alternative these days. You may want to read the PHP Passwords FAQ. You may want to replace the md5() with the password_hash() or crypt() functions, and importantly, salt your passwords.
Even $salt = 'r4nd0m57r1n6'; md5($password.$salt); is better than a straight-up md5($password);, should your user database ever become compromised. Though not very much better, given how computationally light-weight MD5 is to crack. (Billions of attempts per second.) At least it's not a straight-up rainbow table lookup when it's salted.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I have thinking of this from past an hour, but unable to understand the best way to add update, Insert feature using a class in PHP.
I have an employee table which has around 5 columns, so i have created different properties for it in my Employees Class.
Now i am unable to understand where to set these Values, in __costructor(), in add_employees() method or some other way..
If i set them in __constructor() then user must remember and follow the same pattern in writing arguments that i followed in writing the parameters.
I am unable to understand that is it a right way or should i use different approach to do this.
I have searched this on net, and found very complex examples, where people are taking argument in form of array, separating it, add loop in it and then put it into database.
Class Employees(){
public $db;
public $conn;
public $emp_id;
public $first_name;
public $last_name;
public $gender;
public $added_by;
public function __construct(){
$this->db= new DatabaseConnection('localhost', 'root', '', 'employeesmanagement');
$this->conn= $this->db->connectDb();
}
public function get_employees(){
}
public function add_employees(){
}
I've written a class before, that does only things related to database things. Here is the class
/**
* CLASS: Database
*
* Description: This class deals with all database connection
* across the website. If any class needs to use the database
* it has to extends this one.
*
* #author: Andre Ferraz
* #copyright: ^
* #version: 2.0
*/
class Database
{
/**
* Holds Class Object instance
*
* #var Object
* #access: Private
* #static
*/
private static $_instace;
/**
* Holds PDO Object
*
* #var PDO
* #access: Private
*/
private $_pdo;
/**
* Used to keep track of how many columns
* has been found!
*
* #var int
* #access: Private
*/
private $_count;
/**
* Holds data from database
*
* #var array
* #access: Private
*/
private $_results = array();
/**
* Description: Instantiates PDO object
*
* #access: Protected
*/
protected function __construct()
{
$host = Config::get("database:host");
$user = Config::get("database:username");
$pass = Config::get("database:password");
$dbna = Config::get("database:dbname");
try
{
$this->_pdo = new PDO("mysql:dbname=".$dbna.";host=".$host.";", $user, $pass);
}
catch(PDOException $e)
{
Redirect::to(500);
}
}
/**
* Description: Gets data from the database
*
* #access: protected
* #return boolean
*/
protected function get($table, $columns, $condition = null)
{
if($condition != null)
{
$query = $this->_pdo->prepare("SELECT $columns FROM $table WHERE $condition");
if($query->execute())
{
$this->_count = $query->rowCount();
if($this->_count > 0)
{
$this->_results = $query->fetchAll();
return true;
}
return false;
}
}
return false;
//#todo condition == null
}
/**
* Description: Very similar to get function, but
* instead it just checks if data exists without storing
* any data.
*
* #access: protected
* #return boolean
*/
protected function query($table, $columns, $condition = null)
{
if($condition != null)
{
$query = $this->_pdo->prepare("SELECT $columns FROM $table WHERE $condition");
if($query->execute())
{
$this->_count = $query->rowCount();
return(($this->_count > 0)? true : false);
}
}
return false;
//#todo condition == null
}
/**
* Description: Updates information on the database
*
* #access: protected
*/
protected function update($table, $CV = array(), $condition)
{
if($CV !=null)
{
$columns = '';
$x = 1;
foreach($CV as $key => $value)
{
$columns .= "$key='$value'";
if($x < count($CV))
{
$columns .= ",";
}
$x++;
}
$query = $this->_pdo->prepare("UPDATE $table SET $columns WHERE $condition");
if($query->execute())
return true;
else
return false;
}
return false;
}
/**
* Description: Inserts data into database
*
* #access: protected
*/
protected function insert($table, $CV = array())
{
if($CV !=null)
{
// Join array elements with a string
$columns = implode(", ", array_keys($CV));
$values = '';
$x = 1;
// Put array key values into variables
foreach($CV as $value)
{
$values .= "'".$value."'";
if($x < count($CV))
{
$values .= ', ';
}
$x++;
}
$query = $this->_pdo->prepare("INSERT INTO $table ($columns) VALUES({$values})");
// Check execution is successful
if($query->execute())
return true;
else
return false;
}
return false;
}
/**
* Description: Deletes data from the database
*
* #access: protected
*/
protected function delete($table, $condition = null)
{
if($condition != null)
{
$query = $this->_pdo->prepare("DELETE FROM $table WHERE $condition");
if($query->execute())
return true;
else
return false;
}
else
{
$query = $this->_pdo->prepare("DELETE FROM $table");
if($query->execute())
return true;
else
return false;
}
}
protected function getResults()
{
return $this->_results;
}
/**
* Description: Singleton pattern, prevents multiple
* instantiations of the same class.
*
* NOTE: This is not needed. Only for "show of"
*
* #access: public
* #static
* #return Object
*/
public static function instance()
{
if(isset(self::$_instace))
return self::$_instace;
else
self::$_instace = new self;
}
}
Which other classes like User class would extend and use all the necessary function from the database to get data related to the user. Have a look at the project. There are some bugs in a few classes (which I can't be bothered to fix at this point), but database class is working fine. I don't mind if you get reference from it.
Visit my github for the full project.
Github
I have created a timeclock system for a website admin area I am working on. But I want to use a class to handle the code in a better way so I am starting over. So far I have 2 classes. One to handle the database connection and the queries to the database through PDO.
When starting the class for the timeclock (Which I am having to build from scratch) I am getting close because I am no longer receiving errors when I load the page. But the results of the query are not right as I should be returning "true" instead of NULL for a record coming from the database. Can someone please help me understand what I am doing wrong.
My Database class is like so(From GitHub)...
/**
* DB - A simple database class
*
* #author Author: Vivek Wicky Aswal. (https://twitter.com/#!/VivekWickyAswal)
* #git https://github.com/indieteq/PHP-MySQL-PDO-Database-Class
* #version 0.2ab
*
*/
require("Log.class.php");
class DB
{
# #object, The PDO object
private $pdo;
# #object, PDO statement object
private $sQuery;
# #array, The database settings
private $settings;
# #bool , Connected to the database
private $bConnected = false;
# #object, Object for logging exceptions
private $log;
# #array, The parameters of the SQL query
private $parameters;
/**
* Default Constructor
*
* 1. Instantiate Log class.
* 2. Connect to database.
* 3. Creates the parameter array.
*/
public function __construct()
{
$this->log = new Log();
$this->Connect();
$this->parameters = array();
}
/**
* This method makes connection to the database.
*
* 1. Reads the database settings from a ini file.
* 2. Puts the ini content into the settings array.
* 3. Tries to connect to the database.
* 4. If connection failed, exception is displayed and a log file gets created.
*/
private function Connect()
{
$host = 'localhost';
$username = 'root';
$password = '';
$dbname = 'acro_1986';
//$this->settings = parse_ini_file("settings.ini.php");
$dsn = 'mysql:dbname='.$dbname.';host='.$host.'';
try
{
# Read settings from INI file, set UTF8
$this->pdo = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
# We can now log any exceptions on Fatal error.
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# Disable emulation of prepared statements, use REAL prepared statements instead.
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
# Connection succeeded, set the boolean to true.
$this->bConnected = true;
}
catch (PDOException $e)
{
# Write into log
echo $this->ExceptionLog($e->getMessage());
die();
}
}
/*
* You can use this little method if you want to close the PDO connection
*
*/
public function CloseConnection()
{
# Set the PDO object to null to close the connection
# http://www.php.net/manual/en/pdo.connections.php
$this->pdo = null;
}
/**
* Every method which needs to execute a SQL query uses this method.
*
* 1. If not connected, connect to the database.
* 2. Prepare Query.
* 3. Parameterize Query.
* 4. Execute Query.
* 5. On exception : Write Exception into the log + SQL query.
* 6. Reset the Parameters.
*/
private function Init($query,$parameters = "")
{
# Connect to database
if(!$this->bConnected) { $this->Connect(); }
try {
# Prepare query
$this->sQuery = $this->pdo->prepare($query);
# Add parameters to the parameter array
$this->bindMore($parameters);
# Bind parameters
if(!empty($this->parameters)) {
foreach($this->parameters as $param)
{
$parameters = explode("\x7F",$param);
$this->sQuery->bindParam($parameters[0],$parameters[1]);
}
}
# Execute SQL
$this->succes = $this->sQuery->execute();
}
catch(PDOException $e)
{
# Write into log and display Exception
echo $this->ExceptionLog($e->getMessage(), $query );
die();
}
# Reset the parameters
$this->parameters = array();
}
/**
* #void
*
* Add the parameter to the parameter array
* #param string $para
* #param string $value
*/
public function bind($para, $value)
{
$this->parameters[sizeof($this->parameters)] = ":" . $para . "\x7F" . utf8_encode($value);
}
/**
* #void
*
* Add more parameters to the parameter array
* #param array $parray
*/
public function bindMore($parray)
{
if(empty($this->parameters) && is_array($parray)) {
$columns = array_keys($parray);
foreach($columns as $i => &$column) {
$this->bind($column, $parray[$column]);
}
}
}
/**
* If the SQL query contains a SELECT or SHOW statement it returns an array containing all of the result set row
* If the SQL statement is a DELETE, INSERT, or UPDATE statement it returns the number of affected rows
*
* #param string $query
* #param array $params
* #param int $fetchmode
* #return mixed
*/
public function query($query,$params = null, $fetchmode = PDO::FETCH_ASSOC)
{
$query = trim($query);
$this->Init($query,$params);
$rawStatement = explode(" ", $query);
# Which SQL statement is used
$statement = strtolower($rawStatement[0]);
if ($statement === 'select' || $statement === 'show') {
return $this->sQuery->fetchAll($fetchmode);
}
elseif ( $statement === 'insert' || $statement === 'update' || $statement === 'delete' ) {
return $this->sQuery->rowCount();
}
else {
return NULL;
}
}
/**
* Returns the last inserted id.
* #return string
*/
public function lastInsertId() {
return $this->pdo->lastInsertId();
}
/**
* Returns an array which represents a column from the result set
*
* #param string $query
* #param array $params
* #return array
*/
public function column($query,$params = null)
{
$this->Init($query,$params);
$Columns = $this->sQuery->fetchAll(PDO::FETCH_NUM);
$column = null;
foreach($Columns as $cells) {
$column[] = $cells[0];
}
return $column;
}
/**
* Returns an array which represents a row from the result set
*
* #param string $query
* #param array $params
* #param int $fetchmode
* #return array
*/
public function row($query,$params = null,$fetchmode = PDO::FETCH_ASSOC)
{
$this->Init($query,$params);
return $this->sQuery->fetch($fetchmode);
}
/**
* Returns the value of one single field/column
*
* #param string $query
* #param array $params
* #return string
*/
public function single($query,$params = null)
{
$this->Init($query,$params);
return $this->sQuery->fetchColumn();
}
/**
* Writes the log and returns the exception
*
* #param string $message
* #param string $sql
* #return string
*/
private function ExceptionLog($message , $sql = "")
{
$exception = 'Unhandled Exception. <br />';
$exception .= $message;
$exception .= "<br /> You can find the error back in the log.";
if(!empty($sql)) {
# Add the Raw SQL to the Log
$message .= "\r\nRaw SQL : " . $sql;
}
# Write into log
$this->log->write($message);
return $exception;
}
}
My Timeclock class...
class Timeclock {
public $user_id;
public function __construct($user_id) {
$this->user_id = $user_id ;
$this->db = new Db();
//$this->clocked_in = is_user_clocked_in($user_id);
}
public function is_user_clocked_in(){
$result = $this->db->query("SELECT * FROM timeclock WHERE user_id = :user_id AND time_out IS NULL", array("user_id"=>$this->user_id));
if ( count ( $result ) > 0 ){
return $result[0];
}else{
return null;
}
}
}
And I am calling it like so...
if (isset($_SESSION['admin'])) {
$_user_id = $_SESSION['admin'][0]['user_id'];
// calls action and determines case
if (isset($_POST['action'])) {
$action = $_POST['action'];
} else if (isset($_GET['action'])) {
$action = $_GET['action'];
} else {
$action = 'home';
}
$action = strtolower($action);
switch ($action) {
case 'home':
$timeclock = new Timeclock($_user_id);
$user = new Timeclock($timeclock->user_id);
$clocked_in = $user->is_user_clocked_in();
include ('dashboard.php');
break;
}
}
Also, is it possible to have every function in the class (Once its done) run one after the other and fill in the declared variables at the top (Once I have added them of course) so I can just call the class and have it run through once? Or will I have to call each function individually on demand?
Thanks for the attempt to help #Ohgodwhy. $clocked_in was returning an array because I asked it to select all columns in the table. So when there was a result, it was an array. I changed the return of the function to return true instead of $result[0] because I only need to know if the user is logged in. I could have probably just changed the query to select that column as well. After doing that, it worked great until I provided a value for the table field (Making the user clocked_in). I then got a Undefined offset:0 error because I was trying to call the value of $result[0] when there was no array indexed because the query obviously returns array(0); I just changed the count to check to see if $result exists.
updated code is as follows in case someone comes across this
Timeclock Class
class Timeclock {
public $user_id;
public function __construct($user_id) {
$this->user_id = $user_id ;
$this->db = new Db();
//$this->clocked_in = is_user_clocked_in($user_id);
}
public function is_user_clocked_in(){
$result = $this->db->query("SELECT * FROM timeclock WHERE user_id = :user_id AND time_out IS NULL", array("user_id"=>$this->user_id));
if ( count ($result) > 0 ){
return true;
}else{
return null;
}
}
}
So in php im trying to get a public variable in the database class that connects to a database when the class is created. Like so -
<?php
class database {
public $_link;
public function __construct (){
$this->_link = new PDO("mysql:host=localhost; dbname=swinkidc_student", "swinkidc_student", "");
}
}
and...
<?php
class user{
private $db;
public function __construct() {
$this->db = new database;
}
/**
* Returns the ID of a user.
* #param string $user
* #return mixed
*/
public function getUserID($user){
$query = $_link->prepare("SELECT `user_id` FROM `users` WHERE `username` = :user");
$query->bindParam(":user", $user);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['user_id'];
}
/**
* Checks if given user is active.
* #param string $user
* #return bool Returns true/false if user is active.
*/
public function isUserActive($user){
}
}
If I extened database in user I can obviously refer to _link if i make it private and it will work, however, I don't think that i should inherit just for getting something like that..
Whenever i try the about i get :
Fatal error: Call to a member function prepare() on a non-object in /home/swinkidc/public_html/studentreach/core/authentication/user.php on line 18
As said, if i try the inheritance way it will work, I just don't feel like thats a great way of doing something like this.
Any suggestions please?
Thanks!
link is actually a property of database and not a property of user. Therefore use:
$query = $this->db->_link->...
You are just missing the $this->db-> before:
$query = $this->db->_link->prepare("SELECT `user_id` FROM `users` WHERE `username` = :user");
Now, it should work; you're trying to access it directly. First access the Database-link holding object through the $this->db and only then access the property $_link.
If you need $_link as a variable because it's shorter, assign the object $this->db->_link to $_link:
$_link = $this->db->_link;
At the beginning of your getUserID function, then it'll work too.
Try, in the User class, something as:
<?php
class user{
private $db;
public function __construct() {
$this->db = new database;
}
/**
* Returns the ID of a user.
* #param string $user
* #return mixed
*/
public function getUserID($user){
$query = $this->db->_link->prepare("SELECT `user_id` FROM `users` WHERE `username` = :user");
$query->bindParam(":user", $user);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['user_id'];
}
/**
* Checks if given user is active.
* #param string $user
* #return bool Returns true/false if user is active.
*/
public function isUserActive($user){
}
}
This can works as _link is not a attribute of User, but of database, and, if you extend the class database, it's acessible from the methods in link (at least in Java, i not know if the same way works for PHP), but not if you just instantiate it. It's just basic OOP.
Good luck.
Better to invert dependencies and wrap $db.
class database {
private $_link;
public function __construct(\PDO $pdo) {
$this->_link = $pdo;
}
public function getLink() {
return $this->_link;
}
//other useful stuff
}
class user {
private $_db;
public function __construct(\database $db) {
$this->_db = $db;
}
public function getDB() {
return $this->_db;
}
/**
* Returns the ID of a user.
* #param string $user
* #return mixed
*/
public function getUserID($user) {
$query = $this->getDB()->getLink()->prepare("SELECT `user_id` FROM `users` WHERE `username` = :user");
$query->bindParam(":user", $user);
$query->execute();
$result = $query->fetch(PDO::FETCH_ASSOC);
return $result['user_id'];
}
/**
* Checks if given user is active.
* #param string $user
* #return bool Returns true/false if user is active.
*/
public function isUserActive($user) {
}
}
Usage
$user = new \user(new \database(new \PDO("mysql:host=localhost; dbname=swinkidc_student", "swinkidc_student", "")));
Now you can configure your db connection outside of the class.
I'm trying to learn oop. I'm working with PHP-MySQL. And I have troubles about database jobs with oop way (save, update, get etc.).
Let me explain it with an example project.
Lets say I want to make a site with multiple user types. I have a single database table with an enum "type" field. And I made classes like these:
abstract class User {
//common properties, functions etc.. like id, username.
}
class Admin extends User {
protected $type = "ADMIN";
//I want here to have admin specific fields, functions etc...
}
...and some other user types like that. Here is the thing. I want a common class that can save and update objects into database. What's the way to do that? I'll make an object like $user = new User(); bla.. bla.. and I'll say "Save this user" but how? Do I have to make functions for each of my classes that have specific SQL statements like "INSERT INTO table(name, pass, etc) VALUES ('name', 'pass', etc)"?
Another point is I want a common factory class that returns me an object. An example I'll say "Get me the user which have this id and instantiate it with admin class if that user is an admin or the other classes like that".
And I need some help about "how to instantiate like mysqli_fetch_assoc() result with objects". That returns an array. Do I need to do like "$object->setId(returned_array["id"])"?
I've looked some books like PHP in Action, PHP Objects, Patterns and Practice but couldn't find this database specific topics. I hope I could explained it well and sorry for my bad English :)
Here is an example PDO CRUD Class & example usage, hope it points you in the right direction:
<?php
/*** a new crud object ***/
$crud = new crud();
/*** The DSN ***/
$crud->dsn = "mysql:dbname=yourDB;host=localhost";
/*** MySQL username and password ***/
$crud->username = 'username';
$crud->password = 'password';
/*** array of values to insert ***/
$values = array(array('user'=>'bob', 'some_colum'=>'somevalue'));
/*** insert the array of values ***/
$crud->dbInsert('users', $values);
/*** select all records from table ***/
$records = $crud->rawSelect('SELECT * FROM users');
/*** fetch only associative array of values ***/
$rows = $records->fetchAll(PDO::FETCH_ASSOC);
/*** example display the records ***/
foreach($rows as $row){
foreach($row as $fieldname=>$value){
echo $fieldname.' = '.$value.'<br />';
}
}
/*** update the user ***/
$crud->dbUpdate('users', 'user', 'bobs_new', 'id', 3);
/*** get the 3rd record ***/
$res = $crud->dbSelect('users', 'id', 3 );
/*** show the results ***/
foreach($res as $row){
echo $row['user'].' = '.$row['some_colum'].'<br />';
}
class crud{
private $db;
/**
* Set variables
*/
public function __set($name, $value)
{
switch($name)
{
case 'username':
$this->username = $value;
break;
case 'password':
$this->password = $value;
break;
case 'dsn':
$this->dsn = $value;
break;
default:
throw new Exception("$name is invalid");
}
}
/**
* #check variables have default value
*/
public function __isset($name){
switch($name)
{
case 'username':
$this->username = null;
break;
case 'password':
$this->password = null;
break;
}
}
/**
* #Connect to the database and set the error mode to Exception
* #Throws PDOException on failure
*/
public function conn(){
isset($this->username);
isset($this->password);
if (!$this->db instanceof PDO)
{
$this->db = new PDO($this->dsn, $this->username, $this->password);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
}
/**
* #select values from table
* #access public
* #param string $table The name of the table
* #param string $fieldname
* #param string $id
* #return array on success or throw PDOException on failure
*/
public function dbSelect($table, $fieldname=null, $id=null){
$this->conn();
$sql = "SELECT * FROM `$table` WHERE `$fieldname`=:id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* #execute a raw query
* #access public
* #param string $sql
* #return array
*/
public function rawSelect($sql){
$this->conn();
return $this->db->query($sql);
}
/**
* #run a raw query
* #param string The query to run
*/
public function rawQuery($sql){
$this->conn();
$this->db->query($sql);
}
/**
* #Insert a value into a table
* #acces public
* #param string $table
* #param array $values
* #return int The last Insert Id on success or throw PDOexeption on failure
*/
public function dbInsert($table, $values){
$this->conn();
/*** snarg the field names from the first array member ***/
$fieldnames = array_keys($values[0]);
/*** now build the query ***/
$size = sizeof($fieldnames);
$i = 1;
$sql = "INSERT INTO $table";
/*** set the field names ***/
$fields = '( ' . implode(' ,', $fieldnames) . ' )';
/*** set the placeholders ***/
$bound = '(:' . implode(', :', $fieldnames) . ' )';
/*** put the query together ***/
$sql .= $fields.' VALUES '.$bound;
/*** prepare and execute ***/
$stmt = $this->db->prepare($sql);
foreach($values as $vals)
{
$stmt->execute($vals);
}
}
/**
* #Update a value in a table
* #access public
* #param string $table
* #param string $fieldname, The field to be updated
* #param string $value The new value
* #param string $pk The primary key
* #param string $id The id
* #throws PDOException on failure
*/
public function dbUpdate($table, $fieldname, $value, $pk, $id){
$this->conn();
$sql = "UPDATE `$table` SET `$fieldname`='{$value}' WHERE `$pk` = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$stmt->execute();
}
/**
* #Delete a record from a table
* #access public
* #param string $table
* #param string $fieldname
* #param string $id
* #throws PDOexception on failure
* */
public function dbDelete($table, $fieldname, $id){
$this->conn();
$sql = "DELETE FROM `$table` WHERE `$fieldname` = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$stmt->execute();
}
}
?>
I think you need an ORM framework. It's hard to create a good one on your own but you can find a few existing frameworks. Be carefulf do not use a framework with active record pattern becouse it's an antipattern.
To fetch objects: http://www.php.net/manual/en/mysqli-result.fetch-object.php
But I also recommend you to use mysqli in OO way:
$resource = new mysqli(/* ... */);
$resource->fetch_object(/* ... */)