Can you get a calling class's variables? - php

I have a class Page that creates an instance of DB, which is named $db.
In the __construct() of Page, I create the new $db object and I pull a bunch of config data from a file.
Now the DB class has a method _connectToDB() which (attempts) to connect to the database.
Is there a way in the DB class to call the parent class's config array? I don't want to make global variables if I don't have to and I don't want to grab the config data twice.
Pseudo code might look something like this...
$dbUsername = get_calling_class_vars(configArray['dbUserName']);

I find that it's often easier to initialise all the "important" objects close to whatever variables they need to know. You could try it this way:
/* Code to get config variables here */
$DB = new DB($config);
/* You might want to delete the database password from $config here */
$Page = new Page($config, $DB);
Doing it this way means you can also do type-checking on the database object if you want to:
class Page {
function __construct(array $config, DBClass $db) { }
}

You can use static if you want to share variables without passing these:
class page{
static $configArray = [];
function doWhatever(){
$db = new DB();
$db->connectToDB();
}
}
class DB{
function connectToDB(){
$dbUsername = page::$configArray['dbUserName'];
}
}
In this case makes sense to have those data as static, because even if you have a multiple instances of the page class, the config parameters should always be the same.
Anyway I think that could be better to construct and keep the $configArray only in the DB class. If you want to call it from outside you can use the same static logic.

Why not pass the config parameters to the connectToDb function or pass the config data to the constructor of the DB class.
And to directory answer the question: you don't know anything about the outside calling context in your current context.

See debug_backtrace() to get information about calling classes or objects.
Then see Reflection to get more information on the properties of a given class or object.
edit: But for what it's worth, I'd also recommend passing the specific parameters you need. Referencing the caller's data probably constitutes Content Coupling.

You can pass the child a reference to the parent. For example:
class Parent {
function __construct() {
$this->myChild = new Child($this);
}
public function doSomething() {}
}
class Child {
function __construct(Parent $parent) {
$parent->doSomething()
}
}
You may find this odd, since you're still in the parent's constructor, and thus your parent object isn't fully constructed yet. But you can still use $this as a reference to yourself, even in the constructor. You just need to be careful that your child doesn't refer to things in the parent that haven't been initialized yet.

In general there is no easy way to do this. but as a general design issue i can see why your avoid global variables, but in the case of application wide configuration it is perfectly reasonable to have global access to the properties.
you can either make your config data global or just pass the needed properties to the db object initialization.

Related

Is it bad practice to instantiate classes inside of functions PHP

I'm in the process of re factoring a lot of code to make it more testable and I have a bunch of useful functions that rely on an instantiated database object.
Things like this:
function id_from_name($table, $name)
{
$db = get_database();
//code that returns an id
}
function username_from_user_id($id)
{
$db = get_database();
//code that returns a username
}
There are a bunch more like id_exists, id_active etc.
Now I'm thinking that this isn't the right thing to do as the object should probably be passed through as an argument? But then that means creating and sending in a new object into each of these functions every time i want to use one.
So really, my questions are: Should I be moving these functions into their own class/library that has access to the database object? and are the examples that I've shown above generally a bad way of doing things?
A better approach would be indeed to make classes. And you would be passing the database object to the constructor and make it an instance variable. That way every function would have access to the database object.
Now the reason why it is considered bad to instantiate e.g. your database object in every function, is because if you decide for example one day to change your datasource, you might need a huge refactor. If you pass your database object into the constructor, you can just pass/inject the right object into the class without any refactor.
...a bit more about DI below...
By passing your objects into the constructors, you also create a more clear API => you know which object depends on the other, you know exactly which class uses your DB object. If you start instantiating it or accessing it in a static way inside the functions like you did, I would have to look through all your classes to see where your DB object is used. One more point, dependency injection forces SRP (single responsibility principle) => if you start injecting too many objects (constructor gets many arguments), you should suspect your class is doing too much than what it should, and start refactoring.
You can create a class Table_Adapter and instantiate database object inside its constructor:
class Table_Adapter
{
protected $db;
public function __construct()
{
$db = get_database();
}
}
Then you create a child class Items_Table_Adapter' that extendsTable_Adapterand put their all methods related toItems` table.
class Items_Table_Adapter extends Table_Adapter
{
public function item_by_id($id)
{
}
}
Then you use it like:
$tableAdapter = new Items_Table_Adapter();
$item = $tableAdapter->item_by_id(1);
Try something like:
class YourClass{
public static function get_database(){
// your creation
return $db;
}
public function id_from_name($table, $name)
{
/* your code */
//code that returns an id
}
public function username_from_user_id($id)
{
/* your code */
}
}
so you could just use it this way:
$db = YourClass::get_database();
$result = $db->id_from_name($table, $name);
It is certainly recommended that you have the option to swap out your database connection.
Now, if your function get_database() looks like this:
function get_database() {
static $db;
if (!$db)
$db = new \mysqli(...);
return $db;
}
Then you really, really should change it to a wrapper around a class, looking like this:
function get_database_manager() {
static $dbmgr;
if (!$dbmgr)
$dbmgr = new DbManager;
return $dbmgr;
}
function get_database() {
return get_database_manager()->getCurrentConnection();
}
where DbManager has an instance attribute with the current connection that is returned with getCurrentConnection(). If you want to swapt out the connection, do something like get_database_manager()->setConnection($newConn). Problem solved :)
I'll leave the downsides of static programming here (it remains with many examples in this thread): http://kunststube.net/static/
as well as the common method to get rid of that (we have another approach here): http://en.wikipedia.org/wiki/Dependency_injection

Php global variable range?

update:
the code i put below will be invoked by a form on other webpage. so that's why I didn't made a instance of a obj.
More detail code:
$serverloc='serverURL';
class Aclass{
function push(){
global $serverloc;
echo $serverloc;
funNotinClass();
}
function otherFunction(){
echo $serverloc;
}
}
funNotinClass(){
echo $serverloc;
}
There is a Class contains 2 functiona "push()" and "otherFunction()" and there is independent function "funNotinClass()" and push() calls it. The class is for a web form in other page. When user click submit the form call the class and use the push() function. A weird thing I found is that the global var $serverloc is invisible to push() and funNotinClass()(they don't print out any thing), but otherFuction() which is a function just like puch() inside of the Aclass can just use the $serverloc(I dont even add global in front of it). How strange....anyone know what is the reason caused this?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I read many information about the scope of a global var in php.
they all say a global var is defined outside of function or class and you can use it by using global this key word.
So this is my code
$serverloc='serverURL';
class Aclass{
function Afunction(){
global $serverloc;
echo $serverloc;
}
}
but when I run this class it didn't print anything out.
Is that because I did something wrong or global var just doesn't work this way. Since all the example I read before are just access a global var in functions directly not a function in a class
As per DaveRandom's comment - you haven't actually made an instance of an Aclass object.
This works and displays data as expected:
<?php
$serverloc='serverURL';
class Aclass{
global $serverloc;
function Afunction()
{
echo $serverloc;
}
}
$me = new Aclass();
$me->Afunction(); // output: serverURL
?>
Edit: DaveRandom seems to post asnwers as comments. Go Vote up some of his other answers, the rep belongs to him not me. I am his ghostwriter tonight.
If it is class globals you are after you could do like
class myClass
{
private $globVar = "myvariable";
function myClass()
{
return $this->globVar;
}
}
but when I run this class it didn't print anything out
You don't run classes, in the sense of executing them. A class is a just a data structure that holds data and functions related (called methods).
As most traditional data structures, you create instances of them (called objects), and then you execute actions on them. One way to execute actions on objects (instances of classes), is to pass a message for it to do something: that is calling a method.
So, in your case you could do:
$obj = new Aclass(); // create an object, instance of Aclass
$obj->Afunction(); // ask it to perform an action (call a method)
Having said that, sometimes you want to create a class only for grouping related functions, that never actually really share data within an object. Often they may share data through a global variable (eg.: $_SERVER, $_GET, etc). That may be the case of your design right there.
Such classes can have its methods executed without never instantiating them, like this:
Aclass::Afunction();
While relying on global variables is usually an indicator of quick'n dirty design, there are cases in which it really is the best trade-off. I'd say that a $serverlocation or $baseurl may very well be one of these cases. :)
See more:
The basics on classes and objects in the PHP manual
About the static keyword

$this->class->function(): how does it work?

I've been programming in PHP for several years and I've only just recently begun to look at object oriented code. Now I understand classes and such:
class Myclass {
public function __construct() {
}
}
and all that good stuff... I also understand creating functions and calling in my index.php:
$someVar = new Myclass;
One thing I've been trying to understand, being that i've recently looked at codeigniter and I like one thing about it and want to try and accomplish the same thing without actually using codeigniter.
in code igniter they have the variable $this appear to be their class variable. But by using that, you're able to call from multiple classes all at once.. such as:
$this->load->module(); which is in one class file..
$this->db->query(); which is in another class file.
I've searched google for the last few days trying to figure out how to do this same thing where each class would have the correlation between them all allowing me to run $this->class_name->function_name in my projects instead of creating a new variable for each class or for the sake of a clean index file, having every function in a single class file.
Any information (aside from buy this book - as that isn't an option for me) is greatly appreciated and I will thank you now (and will probably thank you again later just for good measure).
I've been reading you and Phil's comments. First off, you can't use $this on index.php. $this can only be used in the context of an object. So you could do,
$someVar = new Myclass;
...
$someVar->db->something();
...instead.
I'm not entirely sure what you mean by "read classes," but you can assign members to MyClass exactly as Phil indicates:
class MyClass {
private $inj;
function __construct(Injected $inj) {
$this->injected = $inj;
}
}
Note that the private $inj declaration is not mandatory, but skipping it is dangerous. Any non-declared members added are automatically public, and this can potentially screw with you if you rely on magical get. I would declare the properties you need.
Finally, you either need to include all class definitions you will use, use a function like load_class(), or use autoloading. If Injected above is not a declared class, you will get an error when trying to create one. load_class() almost certainly includes the class definition somehow.
The load and db references are class properties which are themselves other objects with public module() and query() methods respectively. For example
class MyClass
{
/**
* #var CI_DB
*/
private $db;
/**
* #var Loader
*/
private $load;
public function __construct(CI_DB $db, Loader $load)
{
$this->db = $db;
$this->load = $load;
// PHP also allows you to add arbitrary object properties
// that receive a "public" visibility
// Please note, this is not a good practice
$this->foo = new Foo;
}
public function someMethod()
{
$this->load->module();
$this->db->query();
// You can also use foo even though it is not defined
// in the class properties
$this->foo->something();
}
}
See http://www.php.net/manual/en/language.oop5.properties.php
Adding the foo property like we did is dangerous. Because it receives the public visibility, it may be altered externally to the class.
$class = new MyClass($db, $load);
$class->foo = new Bar;
$class->someMethod(); // will break if Bar does not contain a something() method

Understanding PHP Inheritance

I understand the basic principles of inheritance in OOP, but I have a specific thing I am trying to do and want advice on how best to do it.
Lets say I have a core class:
class Core {
....
}
and I also have 2 or more other classes that extend this functionality
class MyClass1 extends Core {
....
}
class MyClass2 extends Core {
....
}
and I also have a database class in which I perform my queries, I want to pass an instantiated object of the database class (possibly by reference) to each one of my classes. One of the reasons for this would be to store a list or count of the queries that page as executed.
How should / can I go about this?
You could pass your instance of your database object to a constructor for your classes :
class Core
protected $db;
public function __construct(Your_Db_Class $database) {
$this->db = $database;
}
}
And, then, from your methods, work with $this->db, to access your database.
Of course, when instanciating your classes, you'll have to specify the database object :
// somewhere, instanciate your DB class
$db = new Your_Db_Class();
// And, then, when instanciating your objects :
$obj = new MyClass1($db);
Another way would be to use the Singleton design pattern, so there can be only one instance of your database class.
Probably a bit easier to setup ; but less easy to unit-test, after.
You could pass the database object as a parameter to the __construct function of your class, and then in said function assign the db object to member of the class, for instance $this->database_handler.
Another possibility is to work with a global variable that is your database object, but global variables are evil for many reasons, so let's disregard that.
Another note: By default, all objects are passed by reference, so you don't need to worry about that.

How to avoid using PHP global objects?

I'm currently creating blog system, which I hope to turn into a full CMS in the future.
There are two classes/objects that would be useful to have global access to (the mysqli database connection and a custom class which checks whether a user is logged in).
I am looking for a way to do this without using global objects, and if possible, not passing the objects to each function every time they are called.
You could make the objects Static, then you have access to them anywhere. Example:
myClass::myFunction();
That will work anywhere in the script. You might want to read up on static classes however, and possibly using a Singleton class to create a regular class inside of a static object that can be used anywhere.
Expanded
I think what you are trying to do is very similar to what I do with my DB class.
class myClass
{
static $class = false;
static function get_connection()
{
if(self::$class == false)
{
self::$class = new myClass;
}
return self::$class;
}
// Then create regular class functions.
}
What happens is after you get the connection, using $object = myClass::get_connection(), you will be able to do anything function regularly.
$object = myClass::get_connection();
$object->runClass();
Expanded
Once you do that static declarations, you just have to call get_connection and assign the return value to a variable. Then the rest of the functions can have the same behavior as a class you called with $class = new myClass (because that is what we did). All you are doing is storing the class variable inside a static class.
class myClass
{
static $class = false;
static function get_connection()
{
if(self::$class == false)
{
self::$class = new myClass;
}
return self::$class;
}
// Then create regular class functions.
public function is_logged_in()
{
// This will work
$this->test = "Hi";
echo $this->test;
}
}
$object = myClass::get_connection();
$object->is_logged_in();
You could pass the currently global objects into the constructor.
<?php
class Foo {
protected $m_db;
function __construct($a_db) {
$this->m_db = $a_db;
}
}
?>
I recently revamped my framework in preparation for the second version of our company's CMS. I undid a huge amount of the things I made static in order to replace them with normal objects. In so doing, I created a huge amount of flexibility that used to rely on me going through and hacking into core files. I now only use static constructs when the only alternative is global functions, which is only related to low-level core functionality.
I'm going to show a few lines of my bootstrap.php file (all of my requests get sent through that file, but you can achieve the same result by including it at the top of every file) to show you what I mean. This is an pretty hefty version of what you'd probably use in your situation, but hopefully the idea is helpful. (This is all slightly modified.)
//bootstrap.php
...
// CONSTRUCT APPLICATION
{
$Database = new Databases\Mysql(
Constant::get('DATABASE_HOST'),
Constant::get('DATABASE_USER'),
Constant::get('DATABASE_PASSWORD'),
Constant::get('DATABASE_SCHEMA')
);
$Registry = new Collections\Registry;
$Loader = new Loaders\Base;
$Debugger = new Debuggers\Dummy; // Debuggers\Console to log debugging info to JavaScript console
$Application = new Applications\Base($Database, $Registry, $Loader, $Debugger);
}
...
As you can see, I have all kind of options for creating my application object, which I can provided as an argument in the constructor to other objects to give them access to these "global" necessities.
The database object is self-explanatory. The registry object acts as a container for object I may want to access elsewhere in the application. The loader acts as a utility for loading other resources like template files. And the debugger is there to handle debug output.
I can, for example, change the database class that I instantiate and, voila I have a connection to a SQLite database. I can change the class of the debugger (as noted) and now all of my debug info will be logged to my JavaScript console.
Okay, now back to the issue. How do you give other objects access to all of this? You simply pass it in an argument to the constructor.
// still bootstrap.php
...
// DISPATCH APPLICATION
{
$Router = new Routers\Http($Application);
$Router->routeUri($_SERVER['REQUEST_URI']);
}
...
Not only that, but my Router (or whatever object I construct with it) is more flexible, too. Now I can just instantiate my application object differently, and my Router will behave differently accordingly.
Well, if you already have some object by which you refer to the blog system, you can compose these objects into that, so that they're $blog->db() and $blog->auth() or whatever.

Categories