Im relatively new to programming, with about 1 years part-time learning experience, im very new to PHP, only about a few weeks and using w3schools to help me
Im now trying to make the switch to OOP style and just cant understand the concept, or rather how and when to apply it and have thus turned here, where I have been a long time lurker instead of contributor for some help and advice.
I would like some help / short explanation how I would write the folloing in a PHP oop way
$sql="SELECT name, lastname, member_nr, joindate, order, creditCardInfo
from members
WHERE member_nr = $'member_nr'";
$result = mysql_query($sql) or die("error");
while($row = mysql_fetch_array){
echo '<h3>Personal Profile</h3>';
echo $name = $row['name'];
echo $lastName = $row['lastname'];
:
:
}
My problem is I dont know if I should create a class for the above, personal profile can be seen as a person right which is an object...? But do I include the HTML part and the mysql_query part etc in my class...Ive just got so many questions when it comes to OOP, its very hard to learn on your own thats why im really appreciate any drop of help or advice I can get on here
Thank you
First you should use mysqli, as all mysql_* are deprecated and are removed from PHP in the future.
Here a basis class with some functions, but you should expand it for your use! And learn what the functions do which got no comments on your own, to learn more.
This makes multiple connections to the database, so reading some stuff about singleton would be good for you too!
<?php
class dbconnection {
private $dbHostname;
private $dbName;
private $dbUsername;
private $dbUserpassword;
private $error;
private $querysuccess;
private $connected;
private $mysqli;
public function __construct() {
$this->dbHostname = 'localhost';
$this->dbName = 'databasename';
$this->dbUsername = 'username';
$this->dbUserpassword = 'password';
$this->connected = false;
}
public function __destruct() {
$this->mysqli->Close();
}
public function connect() {
//establishing a database connection
$this->mysqli = new mysqli($this->dbHostname, $this->dbUsername, $this->dbUserpassword, $this->dbName);
if($this->mysqli->connect_errno) {//When there was an error during the connecting
echo 'Connection Error:<br>'.$this->mysqli->connect_error;
$this->connected = false;
} else {
$this->connected = true;
}
if(!$this->mysqli->set_charset('utf8')) {//Need to be done for some functions!
echo 'Error during seting the charset: <br>'.$this->mysqli->error;
}
}
public function doquery($query_str) {
//method which executes the query
$returnval = false;
if($this->connected && $query_str != '') {
//only when connected AND the query is not empty
$res = $this->mysqli->query($query_str);//this is the equivalent of mysql_query
if($this->error_get() == '') {//Check for an error
$this->querysuccess = true;
} else {
$this->querysuccess = false;
}
$returnval = $res;
} else {
echo 'No database connection, this needs some!';
}
return $returnval;
}
public function query_success() {
return $this->querysuccess;
}
public function affected_rows_get() {
return $this->mysqli->affected_rows;
}
public function error_get() {
return $this->mysqli->error;
}
public function insert_id_get() {
return $this->mysqli->insert_id;
}
public function escape_str($str) {
return $this->mysqli->real_escape_string($str);
}
public function is_connected() {
return $this->connected;
}
public function fetch_assoc(&$res){
return $res->fetch_assoc();
}
public function fetch_assoc_array(&$res){
return $res->fetch_array(MYSQLI_ASSOC);
}
}
This case wouldn't be a good example. You want a class when you want to encapsulate logic of one entity to simplify its use outside the class.
For example:
class Member {
private $id, $name,
$lastname, $member_nr, $joindate, $order, $creditCardInfo;
public function __construct($id = 0) {
if($id != null) $this->loadFromDB();
}
private function loadFromDB() {
//Do db load here...
}
public function Update(){
//Do db update
}
public function Delete() {
//Do db delete...
}
public function GetFromMemberNR($nr) {
//Do select
}
//Additional Functions Verfication
}
In the end you'll have fairly complex code here. But to use it outside you just include the class's php file.
include 'member.php';
$member = new Member();
$member->GetFromMemberNR(2); //Does everything for you inside the function..
There are plenty of tools to help you do database operations easier, but that is the basis of OOP. The main idea is Encapsulation for reusability.
Related
I'm learning how to use classes properly... I'm looking at usercake and most of it makes sense, however I'm not sure what the __construct function is doing. I understand it gets called when you create the class... i.e. $loggedInUser = new loggedInUser();
What does the stuff below do and why do I need it?
function __construct($user, $display, $title, $pass, $email)
{
//Used for display only
$this->displayname = $display;
//Sanitize
$this->clean_email = sanitize($email);
$this->clean_password = trim($pass);
$this->username = sanitize($user);
$this->title = sanitize($title);
if(usernameExists($this->username))
{
$this->username_taken = true;
}
else if(displayNameExists($this->displayname))
{
$this->displayname_taken = true;
}
else if(emailExists($this->clean_email))
{
$this->email_taken = true;
}
else
{
//No problems have been found.
$this->status = true;
}
}
Edit: Here is how the class gets called:
$loggedInUser = new loggedInUser();
$loggedInUser->email = $userdetails["email"];
$loggedInUser->user_id = $userdetails["id"];
$loggedInUser->hash_pw = $userdetails["password"];
$loggedInUser->title = $userdetails["title"];
$loggedInUser->displayname = $userdetails["display_name"];
$loggedInUser->username = $userdetails["user_name"];
$loggedInUser->alerts = array();
It is the constructor function. When you create an instance of that class your constructor function is run.
For example, with your constructor (I don't know your class name).
$class = new MyClass("jdoe", "John Doe", "Standard User", "Passw0rd!","jdoe#example.com");`
This will create a new MyClass and store it in $class.
As for its purpose, it lets you initialize the object to a starting state of some kind. You can populate properties, set default values, or just do nothing. It is really application specific.
EDIT (in response to OP's edit)
I would really suggest keeping your object properties either protected or private and use setter/getters to access that data. You are giving public access to your objects properties, which isn't bad, but it can lead to accidentally changing something you didn't mean to change. Maybe you should consider something like this:
<?php
class LoggedInUser
{
private $id;
private $username;
private $password;
private $displayname;
private $email;
private $title;
public function __construct($id, $username, $password, $displayname, $email, $title)
{
$this->setID($id);
$this->setUsername($username);
$this->setPassword($password);
$this->setDisplayName($displayname);
$this->setEmail($email);
$this->title($title);
}
public function sanitize($var)
{
//Sanitize $var and then...
return $var;
}
public function setID($num)
{
$this->id = $this->sanitize($num);
}
public function setUsername($string)
{
$this->username = $this->sanitize($string);
}
//Keep adding other "set" methods.
}
?>
Then to use this you would do something like:
$loggedin = new LoggedInUser( "arg1", "arg2", "etc..." );
Now your object is setup with the starting state. If you need to change a property later you can always do:
$loggedin->setTitle("Correct Title");
Make sure you create functions to return your properties as well. In the example above your properties are private so a call to $loggedin->title would generate an error in PHP
// Set construct function which will run when your class is called
function __construct($user, $display, $title, $pass, $email)
{
// Sets display name
$this->displayname = $display;
// Sanitizing user inputted data (See SQL injection/XSS attacks)
$this->clean_email = sanitize($email);
$this->clean_password = trim($pass);
$this->username = sanitize($user);
$this->title = sanitize($title);
// Check if any duplicates of the user inputted data exist already in the database
// If any of these checks return true, the status wont be set to true, and further code wont be ran
if(usernameExists($this->username))
{
$this->username_taken = true;
}
else if(displayNameExists($this->displayname))
{
$this->displayname_taken = true;
}
else if(emailExists($this->clean_email))
{
$this->email_taken = true;
}
else
{
// No duplicate information has been found, set status and continue registration
$this->status = true;
}
}
You need it because initialize the object you create.
We are trying to understand the best way to use mysqli/other classes in multiple custom classes so that we don't instantiate a new object every time.
Is the code below the best/correct way of doing this?
The functions are only examples.
Thank you :)
<?php
class Base {
public function __get($name) {
if($name == 'db'){
$db = new mysqli('**', '*s', '*', '*');
$this->db = $db;
return $db;
}
if($name == 'blowfish'){
$blowfish = new PasswordHash(8, true);
$this->blowfish = $blowfish;
return $blowfish;
}
}
}
class A extends Base {
public function validate($username, $password) {
$query = $this->db->query("SELECT * FROM users");
return $query->num_rows;
}
public function password($password)
{
return $this->blowfish->HashPassword($password);
}
}
class PasswordHash {
public function __construct($iteration_count_log2, $portable_hashes) { }
public function HashPassword($password) {
return $password;
}
}
$a = new A;
echo $a->validate('test','test'); // returns number rows count as expected
echo $a->password('password123'); // returns password123 as expected
?>
You are/should probably be more interested in Dependency Injection instead of creating a tight coupling of Base|A and the MySQL database.
Code snippet for class lnemail_fetch
<?php Part of heritage_classes.php
// Declare classes
class lnemail_fetch {
// return string in format "title | factoid"
public $result;
public function get_ln_info()
{
include ("./includes/LOheritage-config.php");
mysql_connect("$dbhost", "$dbuser", "$dbpass") or die(mysql_error());
mysql_select_db("$dbname") or die(mysql_error());
$query = "SELECT * FROM lnemail";
$result = mysql_query($query);
$this->result = $result;
}
}
?>
Code Snippet from larger program It lists a MySQL table
require_once('./includes/heritage_classes.php');
$newlnemail_fetch = new lnemail_fetch;
$newlnemail_fetch->get_ln_info();
$newresult = $newlnemail_fetch->result;
echo "lnemail File display <br />";
while($row = mysql_fetch_array($newresult))
{
echo $row['ln_email']. " | " . $row['ln_date'] . " | " . $row['ln_week'] ;
echo "<br />";
}
Is this use of PHP OOP considered good practice even though it works nicely for now?
I would say no, it's no good use of OOP.
Areas for improvement:
Separate the db connection and query stuff.
Separate the db result handling. Implementing a result object that is iterable will be a good idea.
Not using the mysql extension and switching to mysqli is a very good idea. It will also give you an OOP interface to MySQL for free.
Probably aspects of escaping input inside SQL strings should be considered, but this is undecidable because no such code has been shown.
Will some future release break it?
Yes, because you are using the old and (now) deprecated mysql_* functions.
Code snippet for class lnemail_fetch
The name lnemail is not really a good name for a class, because when I look at it I have no idea what ln means. Also class names are often UpperCamelCased and methods camelCased.
Now to actually look at your code:
When looking at your class it is just a class and currently has nothing to do with OOP. What I would have done is make the $result property private, because currently your is simply some container for data. Also I would introduce another class which will be reponsible for accessing the data from the database (or whatever storage you have). I would also introduce another class to represent a single email and an factory class to build these mail objects. This would look something like the following:
// not sure whether inbox is the correct name, because I don't really have a good idea of what the class represents
class Inbox
{
private $storage;
private $mailFactory;
public function __construct($storage, $mailFactory)
{
$this->storage = $storage;
$this->mailFactory = $mailFactory;
}
public function fetchAllMails()
{
$mailRecordset = $this->storage->fetchAll();
$mails = array();
foreach ($mailRecordset as $mailRecord) {
$mails[] = $this->mailFactory->create($mailRecord);
}
return $mails;
}
}
class InboxStorage
{
private $dbConnection;
public function __construct(\PDO $dbConnection)
{
$this->dbConnection = $dbConnection;
}
public function fetchAll()
{
$stmt = $this->dbConnection->query('SELECT * FROM lnemail');
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}
class Email
{
private $email;
private $date;
private $week;
public function __construct($email, $date, $week)
{
$this->email = $email;
$this->date = $date;
$this->week = $week;
}
public function getEmail()
{
return $this->email;
}
public function getDate()
{
return $this->date;
}
public function getWeek()
{
return $this->week;
}
}
class EmailFactory
{
public function create($record)
{
return new Email($record['email'], $record['date'], $record['week']);
}
}
And you can run it like following:
// initialize all the objects we are going to need
$emailFactory = new EmailFactory();
$dbConnection = new \PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$inboxStorage = new InboxStorage($dbConnection);
$inbox = new Inbox($inboxStorage, $mailFactory);
// run the code
foreach($inbox->fetchAllMails() as $email) {
echo $mail->email . ' | ' . $mail->date . ' | ' . $mail->week . '<br>';
}
It's not really a true class because lnemail_fetch isn't an object. All you are doing is making a container and having to make the container merely to call a function that could be static and return the result rather than assign it.
A better class might include the newer mysqli rather than the dated mysql and work as follows. It makes the rows into objects with the columns being properties (variables;
<?php
class lnemail {
public $ln_emai;
public $ln_date;
public $ln_week;
public static function Fetch($dbhost,$dbuser,$dbpass,$dbname) {
$db = new mysqli($dbhost, $dbuser, $dbpass,$dbname) or die(mysql_error());
$query = "SELECT * FROM lnemail";
$result = $db->query($query);
$returnArr = array();
while($obj = $result->fetch_object('lnemail') {
$returnArr[] = $obj;
}
return $returnArr;
}
}
Then
<?php
require_once("./includes/LOheritage-config.php");
require_once('./includes/heritage_classes.php');
$lnemails = lnemail::Fetch($dbhost,$dbuser,$dbpass,$dbname);
echo "lnemail File display <br />";
foreach($obj as $lnemail) {
echo $obj->ln_email. " | " . $obj->ln_date . " | " . $obj->ln_week;
echo "<br />";
}
I am developing a project in which two portions of webpage frequently change and fetch recent data. I have some confusion about whether to use mysql_connect or mysql_pconnect? I have one config file that is being included in every page. There is one database connection object which I use for queries. Even when approximately 70 users are online it shows 20,000 connections on my server. Please suggest me the best way to keep a single connection alive from a single user, so there should be 70 connections when there are 70 users online. Currently I'm not using mysql_close method to close connection. Is this the reason it shows these many connections? Your advice will really be appreciated.
A common pattern used in this case is the singleton pattern, here's some rough code.
class DB_Instance
{
private static $db;
public static function getDB()
{
if (!self::$db)
self::$db = new Database();
return self::$db;
}
}
function getSomething()
{
$conn = DB_Instance::getDB();
.
.
.
}
Some examples/references
http://tutorialpedia.org/tutorials/Singleton+pattern+in+PHP.html
http://www.ricocheting.com/static/code/php/mysql-v3/Database.singleton.phps
http://netlinxinc.com/netlinx-blog/53-php/7-applying-the-singleton-pattern-to-database-connections-in-php.html
Here you have my implementation maybe it is useful for you
<?php
class Utils_Sql{
private $count = 0;
private static $sqlObj = null;
/**
* #return Utils_Sql
*/
public static function getSql(){
if(self::$sqlObj===null){self::$sqlObj = new Utils_Sql();}
return self::$sqlObj;
}
private $db;
private function __construct(){
$this->db = mysql_connect(MYSQL_SERVER,DB_LOGIN,DB_PASS);
if($this->db === false){
Utils_Logging_Logger::getLogger()->log(
array("Unable to connect to DB on Mysql_Server:".MYSQL_SERVER." with login:".DB_LOGIN." and pass:".DB_PASS."!")
,Utils_Logging_Logger::TYPE_ERROR
);
}else{
if (!mysql_select_db ( DB_NAME , $this->db )) {
$sql = "CREATE DATABASE " . DB_NAME;
$this->qry($sql);
if (!mysql_select_db ( DB_NAME , $this->db )) {
Utils_Logging_Logger::getLogger()->log(
array("DB: ".DB_NAME." not found"),
Utils_Logging_Logger::TYPE_ERROR
);
}
}
}
mysql_set_charset ('utf8',$this->getConnection());
}
public function getConnection(){return $this->db;}
public function qry($sql,$errType,$errMsg=""){
$this->count++;
// Utils_Logging_Logger::getLogger()->log("<br>$sql<br>",Utils_Logging_Logger::TYPE_LOG);
$ret = mysql_query($sql,$this->getConnection());
if(mysql_error($this->getConnection())){
//Error
$msgs = array(
"mysql_error: (".mysql_error($this->getConnection()).")",
"qry: \"$sql\""
);
if($errMsg!==""){$msgs[]="$errMsg";}
Utils_Logging_Logger::getLogger()->log($msgs,$errType);
}
return $ret;
}
public function getData($sql,$errType=Utils_Logging_Logger::TYPE_ERROR){
$r = $this->qry($sql,$errType);
if($r === false){
Utils_Logging_Logger::getLogger()->log("No Sql Resource, Illegal Query!",$errType);
return false;
}
$ret = array();
while(($data = mysql_fetch_assoc($r))!==false){
$ret[] = $data;
}
if(count($ret)===1){return $ret[0];}
else if(count($ret)>1){return $ret;}
else{
$msgs = array(
"No resulset found.",
"qry: \"$sql\""
);
Utils_Logging_Logger::getLogger()->log($msgs,$errType|Utils_Logging_Logger::TYPE_WARNING);
return false;
}
}
public function getInsertId($sql,$errType=Utils_Logging_Logger::TYPE_ERROR){
$this->qry($sql,$errType);
$ret = mysql_insert_id($this->getConnection());
if(!is_numeric($ret)){Utils_Logging_Logger::getLogger()->log("mysql_insert_id is not numeric!",$errType);}
return $ret;
}
public function getDbName(){return DB_NAME;}
public function __destruct(){
// Utils_Logging_Logger::getLogger()->log("Querys count: '$this->count'",Utils_Logging_Logger::TYPE_LOG);
}
}
Interstellar_Coder is correct, you want to use a singleton/factory solution for your db connection handling. We use here at work and it serves us well.
While Interstallar_Coder's solution is valid, I wrote up a more flexible solution in response to another post.
Destroy db connections:
public function __destruct()
{
foreach (self::$dbos as $key => $dbo) {
self::$dbos[$key] = null;
}
}
More info about PDO connection management.
I have a connection class for MySQL that looks like this:
class MySQLConnect
{
private $connection;
private static $instances = 0;
function __construct()
{
if(MySQLConnect::$instances == 0)
{
//Connect to MySQL server
$this->connection = mysql_connect(MySQLConfig::HOST, MySQLConfig::USER, MySQLConfig::PASS)
or die("Error: Unable to connect to the MySQL Server.");
MySQLConnect::$instances = 1;
}
else
{
$msg = "Close the existing instance of the MySQLConnector class.";
die($msg);
}
}
public function singleQuery($query, $databasename)
{
mysql_select_db(MySQLConfig::DB, $this->connection)
or die("Error: Could not select database " . MySQLConfig::DB . " from the server.");
$result = mysql_query($query) or die('Query failed.');
return $result;
}
public function createResultSet($query, $databasename)
{
$rs = new MySQLResultSet($query, MySQLConfig::DB, $this->connection ) ;
return $rs;
}
public function close()
{
MySQLConnect::$instances = 0;
if(isset($this->connection) ) {
mysql_close($this->connection) ;
unset($this->connection) ;
}
}
public function __destruct()
{
$this->close();
}
}
The MySQLResultSet class looks like this:
class MySQLResultSet implements Iterator
{
private $query;
private $databasename;
private $connection;
private $result;
private $currentRow;
private $key = 0;
private $valid;
public function __construct($query, $databasename, $connection)
{
$this->query = $query;
//Select the database
$selectedDatabase = mysql_select_db($databasename, $connection)
or die("Error: Could not select database " . $this->dbname . " from the server.");
$this->result = mysql_query($this->query) or die('Query failed.');
$this->rewind();
}
public function getResult()
{
return $this->result;
}
// public function getRow()
// {
// return mysql_fetch_row($this->result);
// }
public function getNumberRows()
{
return mysql_num_rows($this->result);
}
//current() returns the current row
public function current()
{
return $this->currentRow;
}
//key() returns the current index
public function key()
{
return $this->key;
}
//next() moves forward one index
public function next()
{
if($this->currentRow = mysql_fetch_array($this->result) ) {
$this->valid = true;
$this->key++;
}else{
$this->valid = false;
}
}
//rewind() moves to the starting index
public function rewind()
{
$this->key = 0;
if(mysql_num_rows($this->result) > 0)
{
if(mysql_data_seek($this->result, 0) )
{
$this->valid = true;
$this->key = 0;
$this->currentRow = mysql_fetch_array($this->result);
}
}
else
{
$this->valid = false;
}
}
//valid returns 1 if the current position is a valid array index
//and 0 if it is not valid
public function valid()
{
return $this->valid;
}
}
The following class is an example of how I am accessing the database:
class ImageCount
{
public function getCount()
{
$mysqlConnector = new MySQLConnect();
$query = "SELECT * FROM images;";
$resultSet = $mysqlConnector->createResultSet($query, MySQLConfig::DB);
$mysqlConnector->close();
return $resultSet->getNumberRows();
}
}
I use the ImageCount class like this:
if(!ImageCount::getCount())
{
//Do something
}
Question: Is this an okay way to access the database? Could anybody recommend an alternative method if it is bad?
Thank-you.
Hey Mike, there's nothing wrong with implementing your own classes to handle database connection, what you have so far is fine, however PHP already provides an interface for handling DB connections regardless of the database manager you are connecting to. I'd recommend you to take a look at it http://www.php.net/manual/en/book.pdo.php since it has mostly all the functionality needed for handling queries, statements, resultsets, errors, and so forth.
Cheers,
M.
I'm not sure that having a class called "ImageCount" is really necessary. If you're going to be working with images - I would simply have a class called "Image" with a static function to get the count of all images, and some other functions to deal with images.
Also, if you try to create a new instance when one exists - how about returning the existing instance instead of using die() to stop the program.