Method chanining and re-use of same class - php

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.

Related

Not quite understanding how to use a constructor it seems

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 />";
}

PHP - Object of class could not be converted to string

I'm trying to print a property of the simple class below. But instead i get the error above. I haven't found an answer on similar questions on here. The error triggers on this line:
echo "$object1 name = " . $object1->name . "<br>";
Using XAMPP on Windows Help?
<?php
$object1 = new User("Pickle", "YouGotIt");
print_r($object1);
$object1->name = "Alice";
echo "$object1 name = " . $object1->name . "<br>"; /* this triggers the error */
class User
{
public $name, $password;
function __construct($n, $p) { // class constructor
$name = $n;
$password = $p;
}
}
?>
There are two things wrong in your code,
You're using local variables in your class constructor, not instance properties. Your constructor method should be like this:
function __construct($n, $p) {
$this->name = $n;
$this->password = $p;
}
Now comes to your error, Object of class could not be converted to string. This is because of this $object in echo statement,
echo "$object1 name = " ...
^^^^^^^^
You need to escape this $object1 with backslash, like this:
echo "\$object1 name = " . $object1->name . "<br>";
In my case the problem was the way I was initializing the class variable.
This was my code :
public function __construct(User $userObj) {
$this->$userObj = $userObj;
}
and I solved it by changing it to the following :
public function __construct(User $userObj) {
$this->userObj = $userObj;
}
The line in the first snippet caused the problem : $this->$userObj = $userObj

Getting a value from a PHP object

I have an object that is returns as below.
print_r($all->getInfo());
//returns the following on browser.
User Object ( [_name:User:private] => Kanye, West [_email:User:private] => kanye#hotmail.com)
I would like to read the name and email and set it to two seperate variables like below.
$email = $all->getInfo()._email;
$name= $all->getInfo()._name;
Any help would be greatly appreciated.
You can do somtething like this to get access to private proprites.
class sampleClass {
private $property1 = 'name';
private $prooerty2 = 'email';
public function getProperty1(){
return $this->property1;
}
public function getProperty2(){
return $this->property2;
}
public function setProperty1($name){
}
$this->property1 = $name;
}
public function setProperty2($email){
$this->property2 = $email;
}
}
To get properties:
$classy = new sampleClass();
echo $classy->getProperty1;
echo $classy->getProperty2;
To set properties:
$classy->setProperty1('name1');
$classy->setProperty2('email2');
You can also use magic methods for __get and __set, more info.
If you want to access the values, you can either set them to public (which may not be the best solution depending on what you're trying to do), or create the __get() magic method
class foo
{
private $value = "foo";
private $value2 = "foo2";
public function __get($name)
{
switch ($name)
{
case "value";
return $this->$name;
default:
return null;
}
}
}
class bar
{
private $value = "bar";
}
$f = new foo;
$b = new bar;
echo "Result:" . $f->value . "<br/>";
echo "Result:" . $f->value2 . "<br/>";
echo "Result:" . $b->value . "<br/>";
this yeilds
Result:foo
Result:
E_ERROR : type 1 -- Cannot access private property bar::$value -- at line 31
Of course, you can also create a function such as get_name() but then you'd have to do that for every private property you want to access.
If you want to control what is and what is not accessible from the __get() method, you can add some logic. This way you know that if you are trying to access a property, it's in the __get() method and not have to try to figure out what the method name is.

PHP OOP code works well. Is it correct practice? Will some future release break it?

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 />";
}

How to instantiate object of $this class from within the class? PHP

I have a class like this:
class someClass {
public static function getBy($method,$value) {
// returns collection of objects of this class based on search criteria
$return_array = array();
$sql = // get some data "WHERE `$method` = '$value'
$result = mysql_query($sql);
while($row = mysql_fetch_assoc($result)) {
$new_obj = new $this($a,$b);
$return_array[] = $new_obj;
}
return $return_array;
}
}
My question is: can I use $this in the way I have above?
Instead of:
$new_obj = new $this($a,$b);
I could write:
$new_obj = new someClass($a,$b);
But then when I extend the class, I will have to override the method. If the first option works, I won't have to.
UPDATE on solutions:
Both of these work in the base class:
1.)
$new_obj = new static($a,$b);
2.)
$this_class = get_class();
$new_obj = new $this_class($a,$b);
I have not tried them in a child class yet, but I think #2 will fail there.
Also, this does not work:
$new_obj = new get_class()($a,$b);
It results in a parse error: Unexpected '('
It must be done in two steps, as in 2.) above, or better yet as in 1.).
Easy, use the static keyword
public static function buildMeANewOne($a, $b) {
return new static($a, $b);
}
See http://php.net/manual/en/language.oop5.late-static-bindings.php.
You may use ReflectionClass::newInstance
http://ideone.com/THf45
class A
{
private $_a;
private $_b;
public function __construct($a = null, $b = null)
{
$this->_a = $a;
$this->_b = $b;
echo 'Constructed A instance with args: ' . $a . ', ' . $b . "\n";
}
public function construct_from_this()
{
$ref = new ReflectionClass($this);
return $ref->newInstance('a_value', 'b_value');
}
}
$foo = new A();
$result = $foo->construct_from_this();
Try using get_class(), this works even when the class is inherited
<?
class Test {
public function getName() {
return get_class() . "\n";
}
public function initiateClass() {
$class_name = get_class();
return new $class_name();
}
}
class Test2 extends Test {}
$test = new Test();
echo "Test 1 - " . $test->getName();
$test2 = new Test2();
echo "Test 2 - " . $test2->getName();
$test_initiated = $test2->initiateClass();
echo "Test Initiated - " . $test_initiated->getName();
When running, you'll get the following output.
Test 1 - Test
Test 2 - Test
Test Initiated - Test

Categories