I am making a minor CMS for specific use at my current job, and I want to do it right... hence OOP.
For this I want to make a database connection class that I can use in my other classes when I need to make database queries. However, for some reason, I keep being stopped at the same point, because I don't seem to be able to get the connection string into my classes.
Whatever changes I make, I end up with "undefined... " something every time.
File - databaseManagement.class.php:
class database {
private $user;
private $pass;
private $db;
private $serv;
private $type;
private $dsn;
private $sqlsrvString;
private $charset;
private $dbIni;
private $options;
public $connectionInfo;
public $dbConn;
public function __construct() {
$this->dbIni = parse_ini_file('settings/database.ini.php');
// ... assign values to individual variables based on values in the database.ini.php file.
$this->user = $this->dbIni['user'];
$this->pass = $this->dbIni['pass'];
$this->db = $this->dbIni['db'];
$this->serv = $this->dbIni['serv'];
$this->type = $this->dbIni['type'];
$this->charset = $this->dbIni['charset'];
$this->connectionInfo = array(
"Database"=>$this->db,
"UID"=>$this->user,
"PWD"=>$this->pass
);
}
public function dbConnect() {
$this->dbConn = sqlsrv_connect($this->serv, $this->connectionInfo);
}
File - smallInfo.class.php
class smallInfo {
function __construct() {
$initDB = new database();
$initDB->dbConnect();
var_dump($initDB);
}
function showDkProgress(){
echo "<hr />";
print_r($initDB->dbConn);
echo "<hr />";
}
Now the var_dump() in smallInfo constructor returns all the expected values, including those of $dbConn, however, I can't seem to access $dbConn in showDkProgress() for use in queries.
Two problems:
Constructors does not set variables (e.g. $variableName) as object attributes. You have to explicitly assign it with $this->variableName assignment.
Unlike Java, the OOP implementation in PHP does not automatically alias attributes. The variable $variableName in a method does not automatically equal to $this->variableName.
So:
class smallInfo {
function __construct() {
$this->initDB = new database();
$this->initDB->dbConnect();
var_dump($this->initDB);
}
function showDkProgress(){
echo "<hr />";
print_r($this->initDB->dbConn);
echo "<hr />";
}
}
In your smallinfo class, initDB is a local variable inside the constructor. It doesn't exists outside the constructor.
Thus, accessing an undefined variable named initDB in showDkProgress yields an error.
You need to make initDB a field.
class smallInfo {
private $initDB;
function __construct() {
$this->initDB = new database();
$this->initDB->dbConnect();
var_dump($this->initDB);
}
function showDkProgress(){
echo "<hr />";
print_r($this->initDB->dbConn);
echo "<hr />";
}
}
Function showDkProgress() is unable to get it since it is only local variable in constructor.
If you embed $initDB in class you should add field for it.
so try eg:
class smallInfo {
public $initDB;
function __construct() {
$this->initDB = new database();
$this->initDB->dbConnect();
function showDkProgress(){
$si = new smallInfo();
echo "<hr />";
print_r($this->initDB->dbConn);
echo "<hr />";
}
Related
I want to have access to a global variable without having to redeclare every function. Here is an example.
$mobile = 1;
class Database
{
private $connect;
function one($argumentarray)
{
global $mobile;
echo $mobile;
}
function two($argumentarray)
{
global $mobile;
echo $mobile;
}
function three($argumentarray)
{
global $mobile;
echo $mobile;
}
}
I have about 100 functions and i need access to the $mobile variable in all of them. Is there a way to do this without having to do global $mobile in every function?
The best way would be to NOT use the global. Design your program in a way that it can live without globals. That's it.
A simple design that comes in mind, might look like:
class Configuration {
protected static $mobile = 1;
// let's say it's write protected -> only a getter.
public static mobile() {
return self::$mobile;
}
}
class Database {
// use the config value
function one() {
if(Configuration::mobile()) {
...
}
}
}
Use the $GLOBALS array.
$GLOBALS["mobile"]
Or you could store the variable in your class - which is cleaner in my opinion.
Well, You could do something like this:
class Database
{
private $connect;
private $mobile;
function __construct() {
#assign a value to $mobile;
$this->mobile = "value";
}
function one($argumentarray)
{
echo $this->mobile;
}
function two($argumentarray)
{
echo $this->mobile;
}
function three($argumentarray)
{
echo $this->mobile;
}
}
Already 5 answers and nobody mentioned static class variables yet.
Copying the variable in the constructor would be wasteful, impractical and hurt readability. What if you need to change the value of this global later on? Every object will be stuck with an obsolete copied value. This is silly.
Since you need a prefix to access this "global" anyway, let it be self:: or Database:: rather than this-> :).
It will
allow you to set the variable directly from the class definition,
not waste memory doing a copy of it for each object
make it accessable from outside if you whish so (if you declare it public)
In your example:
class Database {
static private $mobile = "whatever";
function one($argumentarray)
{
echo self::$mobile;
}
function two($argumentarray)
{
echo self::$mobile;
}
function three($argumentarray)
{
echo self::$mobile;
}
}
If you want to allow modifications from outside :
static private $mobile;
static function set_mobile ($val) { self::$mobile = $val; }
static function get_mobile ($val) { return self::$mobile; }
or
static public $mobile;
Maybe a class attribute, and set it with the constructor?
class myClass {
private $mobile;
/* ... */
}
In the constructor, do:
public function __construct() {
$this->mobile = $GLOBALS['mobile'];
}
And then use:
$this->mobile
from everywhere!
Pass the variable to the constructor, and set a class level variable.
It can then be accessed in the varius methods using $this->
$mobile = 1;
class Database
{
private $connect;
private $mobile
function __construct($mobile){
$this->mobile=$mobile;
}
function one($argumentarray)
{
echo $this->mobile;
}
function two($argumentarray)
{
echo $this->mobile;
}
function three($argumentarray)
{
echo $this->mobile;
}
}
$db = new Database($mobile);
Here is a cut down version of my code with the main class and main function
I need to get the value of 'userName' that input by the user to use it inside 'mainFunction', tried making the 'userName' inside 'myForm' global but that didn't get the value out.
can value of 'userName' be available out side 'mainClass' so that I can use it anywhere?
class mainClass {
function myForm() {
echo '<input type="text" value="'.$userName.'" />';
}
}
function mainFunction () {
$myArray = array (
'child_of' => $GLOBALS['userName']
)
}
class mainClass {
public $username;
function __construct($username){
$this->username = $username;
}
function myForm() {
echo '<input type="text" value="'.$this->userName.'" />';
}
}
function mainFunction () {
$myArray = array (
'child_of' => $this->username;
);
}
can value of 'userName' be available out side 'mainClass' so that I can use it anywhere?
Yes.
First, you need to define a class property like this
class MainClass
{
private $_userName;
public function myForm()
{
echo '<input type="text" value="'.$this->_userName.'" />';
}
}
Look at how you access this property inside the myForm() method.
Then you need to define getter method for this property (or you can make the property public) like this:
class MainClass
{
private $_userName;
public function getUserName()
{
return $this->_userName;
}
public function myForm()
{
echo '<input type="text" value="'.$this->_userName.'" />';
}
}
You can access user name property like this
$main = new MainClass();
$userName = $main->getUserName();
Note that you need an instance of the MainClass class.
I suggest you to start with simple concept and make sure you understand this 100%. Also I would suggest avoid using global variables and more complicated logic with static methods. Try to make it as simple as possible.
Warm regards,
Victor
The below code is a very minimized version of the Codeigniter get_instance method. So in your case you can have somewhere at the beginning this code:
/** Basic Classes to load the logic and do the magic */
class mainInstance {
private static $instance;
public function __construct()
{
self::$instance =& $this;
}
public static function &get_instance()
{
return self::$instance;
}
}
function &get_instance()
{
return mainInstance::get_instance();
}
new mainInstance();
/** ----------------------------------------------- */
and then you can create your global Classes like this:
class customClass1 {
public $userName = '';
function myForm() {
return '<input type="text" value="'.$this->userName.'" />';
}
}
/** This is now loading globally */
$test = &get_instance();
//If you choose to actually create an object at this instance, you can call it globally later
$test->my_test = new customClass1();
$test->my_test->userName = "johnny";
/** The below code can be wherever in your project now (it is globally) */
$test2 = &get_instance();
echo $test2->my_test->myForm()."<br/>"; //This will print: <input type="text" value="johnny" />
echo $test2->my_test->userName; //This will printing: johnny
As this is now globally you can even create your own function like this:
function mainFunction () {
$tmp = &get_instance();
return $tmp->my_test->userName;
}
echo mainFunction();
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 have the following class:
class DB {
private $name;
public function load($name) {
$this->name = $name;
return $this;
}
public function get() {
return $this->name;
}
}
At the moment if I do:
$db = new DB();
echo $db->load('foo')->get() . "<br>";
echo $db->load('fum')->get() . "<br>";
This outputs "foo" then "fum".
However if I do this:
$db = new DB();
$foo = $db->load('foo');
$fum = $db->load('fum');
echo $foo->get() . "<br>";
echo $fum->get() . "<br>";
It always outputs "fum".
I can sort of see why it would do that, but how could I keep the variable seperate to each instance without having to create a new instance of DB?
IF you mean for DB to be some sort of database connectivity object (which I assume you are), multiple instances may not be the best choice. Perhaps something like this may be what you are trying to do:
$db = new DB();
$foo = $db->load('foo')->get();
$fum = $db->load('fum')->get();
echo $foo . "<br>";
echo $fum . "<br>";
If what I think you are trying to do is correct, it may be better to separate your get logic from the DB class into its own Record class.
class Record {
function __construct($name) {
$this->name = $name;
}
function get(){
return $this->name;
}
private $name;
}
class DB {
public function load($name) {
return new Record($name);
}
}
$db = new DB();
$foo = $db->load('foo');
$fum = $db->load('fum');
echo $foo->get() . "<br>";
echo $fum->get() . "<br>";
To keep it separate to each instance, you would, by definition, need to create a new instance... You could do that though in the load() method. Instead of returning $this, you could return a new DB() configured the way you want. Then make the method static.
This is what's called the factory pattern.
What you're experiencing in your code is similar to PHP's pass by reference feature.
When you set the first variable, $foo is equal to the value of $db->name, when you call it again to set it to 'fum', you're setting $fum equal to $db->name. Since you're echoing them both at the end, they're both going to be the last value you set it to.
Try this and see if your results are different.
$db = new DB();
$foo = $db->load('foo');
echo $foo->get() . "<br>";
$fum = $db->load('fum');
echo $fum->get() . "<br>";
When you run $db->load(), create a new object and return that instead of the object you're currently in.
I am a learner, I have a class db to help me connect and fetch results in mySQL.
$set = $db->get_row("SELECT * FROM users");
echo $set->name;
this way i use echo results outside a class.
Now i have created another class name user and it has this function
public function name() {
global $db;
$set = $db->get_row("SELECT * FROM users");
$this->name = $set->name;
}
after initializing the class user, when i try to echo $user->name i dont get expected results.
Note i have declared above var $name; in class user
I'm pretty concerned by several things I see here
The method name name() is terribly uncommunicative as to what the method is supposed to do. Remember, methods are actions - try to give them some sort of verb in their name.
Usage of global in a class (or even usage of global period) when you should be using aggregation or composition.
You don't show any execution examples, but I can only assume you never actually call User::name(), which is why your test is failing
Here's some code that addresses these concerns.
<?php
class DB
{
/* Your methods and stuff here */
}
class User
{
protected $db;
protected $name;
public function __construct( DB $db )
{
$this->db = $db;
}
public function getName()
{
if ( is_null( $this->name ) )
{
$set = $this->db->get_row( "SELECT * FROM users" );
$this->name = $set->name;
}
return $this->name;
}
}
$db = new DB();
$user = new User( $db );
echo $user->getName();
class DB
{
public function get_row($q)
{
# do query and store in object
return $object;
}
}
class User
{
public $name;
public function __construct()
{
$this->name();
}
public function name() {
global $db;
$set = $db->get_row("SELECT * FROM users");
echo "<pre>".print_r($set)."</pre>"; # make sure $set is returning what you expected.
$this->name = $set->name;
}
}
$db = new DB();
$user = new User();
echo $user->name;
I am very much sorry, i figured out that problem was on my part, i was using cookies and had two cookies set which were giving problems :(