Let's say I have the following class:
class SQLMapper{
static find_user_by_id($id){
//sql logic here, using the $_DATABASE global to make a connection
}
}
I could simply call:
global $_DATABASE;
at the top of my function, but I don't want to do that for ALL of my static methods. Is there a way to get a static variable inside my class to reference the global $_DATABASE array?
EDIT: I can't assign it in the constructor, since this is all static, and the constructor is never called.
You can use the super-global array $_GLOBALS to access your $_DATABASE variable. For example:
query( $GLOBALS['_DATABASE'], 'some query' );
Alternatively, write a static function that returns the contents of that variable:
class SQLMapper
{
static function getDatabase()
{
global $_DATABASE;
return $_DATABASE;
}
static function find_user_by_id($id)
{
query( self::getDatabase(), 'some query' );
}
}
If it hurts, its likely that you're
doing it wrong.
First off, without seeing more of your code its impossible to provide a more concrete solution, but I would strongly recommend that you consider rearranging your class structure so that your static functions (it sounds like you've got a long list of them to implement) become non-static.
In essence, you should consider accessing an instantiated instance of SQLMapper and then calling the appropriate method from the instance. Using this paradigm, you could just instantiate a class level property for $_DATABASE which can then be freely referenced by all methods in the class.
For example:
class SQLMapper {
private $_db;
public function __construct()
{
global $_DATABASE;
$this->_db = $_DATABASE;
}
public function find_user_by_id($id) {
$sql = "Select * from User WHERE Id = ?";
$stmt = $this->_db->prepare($sql, $id);
return $stmt->execute();
}
}
With that said, using globals is generally a sign of poor code quality so I would also suggest that you consider taking a more object-oriented approach to your current design and look for tried and true methods for eliminating globals from your application altogether.
I'm not so sure I understand what you mean, sorry, but can you try using the static keyword?
Related
I have what seems like a simple query but I have not found an answer elsewhere.
I have 2 classes, one called DB which essentially connects to the database and can then run queries. I instantiate it at the top of the document $db= new DB; and I can then run a series of queries on the database throughout the page.
The issue I am having is that I want to use this instance within another class I have called User.
I know I can either instantiate again but this does not make sense OR pass the instance of DB when instantiating User, for instance $user = new User($db); but considering the $db instance will be used by most classes I am going to create I am thinking there is a better solution to import it into other classes.
I have looked at the global route but I read it is bad practice + I am getting unexpected global error
class User{
global $db;
public function __construct()
{
var_dump($this-> db);
}//end constructor
}//end class
Since your DB client will be instantiated once and then used everywhere else my initial thought was to recommend passing it as a constructor parameter (dependency injection), but since you are not fan of this approach, I would recommend making your DB client a singleton class, which means it can only be instantiated once and any subsequent attempt would return the same instance everywhere.
You can see a detailed response about singleton classes in PHP at Creating the Singleton design pattern in PHP5.
As a quick example, your DB would look like similar to this:
final class DB
{
public static function getInstance()
{
static $inst = null;
if ($inst === null) {
$inst = new self();
}
return $inst;
}
private function __construct()
{
// your code here ...
}
// your code here ...
}
And then, on your User class you would just get the DB class instance:
class User {
// your code here ...
public function doSomething() {
$db = DB::getInstance();
// your code here ...
}
}
PHP does not handle scopes like Javascript does, your $db is undefined.
The scope of a variable is the context within which it is defined. For the most part all PHP variables only have a single scope. This single scope spans included and required files as well […] Within user-defined functions a local function scope is introduced. Any variable used inside a function is by default limited to the local function scope.
Source: http://php.net/manual/en/language.variables.scope.php
This means that there is only a global scope and a function/method scope in PHP. So, either pass the $db instance into the method as a collaborator
class User{
public function __construct() {}
public function getInfo(Database $db) {
$db->query( /* ... */ );
}
}
$user = new User();
$db = new Database();
$user->getInfo($db);
or pass it in the constructor (dependency injection)
class User{
private $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function getInfo() {
$this->db->query( /* ... */);
}
}
$db = new Database();
$user = new User($db);
$user->getInfo();
Here is my code:
class {
$property = "something";
public static function myfunc() {
return $this->property;
}
}
but PHP throws this:
Using $this when not in object context
I know, the problem is using $this-> in a static method, ok I remove it like this:
class {
$property = "something";
public static function myfunc() {
return self::property;
}
}
But sadly PHP throws this:
Undefined class constant 'property'
How can I access a property which is out of a static method in it?
Generally, you should not do it. Static methods don't have an access to instance fields for a reason. You can do something like this, though:
// define a static variable
private static $instance;
// somewhere in the constructor probably
self::$instance = $this;
// somewhere in your static method
self::$instance->methodToCall();
Note that it will work only for a single instance of your class, since static variables are shared between all instances (if any).
You'll also need to add a bunch of validations (e.g. is $instance null?) and pay attention to all the implementation details that may cause you some troubles.
Anyway, I don't recommend this approach. Use it at your own risk.
Explaination
If you want to use a variable that wont change inside a class you don't want to instanciate, you need to use the static keyword in order to access it later in a method.
Also, you need a name for your class.
And finally, if you didn't specify a keyword as protected or public, you variable may be accessible outside the word, and so the method would be pointless. So I assume you need a protected value in order to use the method to call that variable.
Source-code
class Foo {
protected static $property = 'something';
public function getProperty() {
return self::$property;
}
}
echo Foo::getProperty(); /* will display : something */
echo Foo::$property; /* change from protected to public to use that syntax */
Documentation
PHP : classes.
PHP : static.
PHP : visibility.
I think my question is kind of noobish but i can't figure out how this stuff is working with PHP. It is a autoloaded class and I want to use my logging class for it.
I know I could have $log also as instance variable but I want it like that.
namespace Bal\Blub;
$log = new Logger('activities');
$log->pushHandler(new StreamHandler(__DIR__.'/../../log/activties.log', Logger::DEBUG));
$log->debug("is working here");
class Foo {
public function bar() {
global $log;
$log->debug("this is my message");
}
}
I don't understand why $log has no value in that case...
Edit: Of course I meant global and not public.
Ok so thanks for your code-style advises. I am quite aware that globals are bad practice. Though it was not my question. The answer for it is to use the global also in the namespace scope, which makes sense, but is a little strange if you are used to the PHP scripting approach.
namespace Bal\Blub;
global $log;
$log = new Logger('activities');
$log->pushHandler(new StreamHandler(__DIR__.'/../../log/activties.log', Logger::DEBUG));
$log->debug("is working here");
class Foo {
public function bar() {
global $log;
$log->debug("this is my message");
}
}
it is global $log. not public $log.
but be careful. globals are always evil. if you really need globals, use at least static variables.
<?php
class Globalholder{
public static $log;
}
Globalholder::$log = new Logger(...);
function foo(){
$log = Globalholder::$log;
$log->logsomething("Hello World");
}
global keyword, a static class property acting like a global variable, a singleton class...all can work, but since we're at it, why not trying to avoid bad practices and use DI?
Right now, your Foo class is tightly coupled with the Logger class, and that can make maintaince hard whenever, some day, you're going to change all the reference to the Logger class.
Why not something simpler like:
class Foo
protected $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function bar($string)
{
$this->logger->debug($string);
}
}
$log = new Logger('activities');
$foo = new Foo($log);
$foo->bar("test");
This way you're decoupling (I know this code can be made even better) the two classes, and you're also making the code easy testable by passing a mock Logger object.
I am writing a PHP class and have included a couple static functions for quick access as they are common for use and are simple in function. However they do use an object in them for database access. I will likely be using these static methods from both a static and non-static context throughout my code so I want to be able to test whether the function was called from a static or non-static context so that I can avoid creating a duplicate object if the function was called from a non-static context (this instance object and the one within the function to be used statically). Is there any way that I can test this in the function so that I can use the instance object if the function is called from a non-static context and create it's own object if the function is called from a static context?
Code Example:
class MyClass {
private $db;
function __constuct(){
$this->db = new DBConnect();
}
public static function myFunction(){
if(/* Is static */){
$db = new DBConnect();
} else {
$db =& $this->db;
}
// Do processing with $db, etc.
}
}
When a method is declared as static,
not only is the magic variable $this
unavailable (returns NULL), but it is
impossible to tell if the function was
actually called from a static context.
A backtrace implies that for a static
method, calling $object->method() is
internally translated to
className::method() at run time.
http://php.net/manual/en/language.oop5.static.php
You can check for non-static access only if you do not force the method to be static-only. Leave out the static keyword from the function declaration and test with:
public function myFunction(){
if(!isset($this)) {
(It can still be called as static function, even though you did not declare it as such.)
Bottom line - don't make classes which contain only static functions. It is not OOP, it is just your old procedural code masquerading as oop
If you are executing functions statically, there is no $this, as there is no object.
You will end up making the private $db a static variable too , and use it as self::$db.
That said, i would recommend to drop this pattern and write something like :
class Foobar
{
protected $_connection = null;
public function __construct( DBConnect $db )
{
$this->_connection = $db;
}
public function some_function()
{
$db = $this->_connection;
// Do processing with $db, etc.
}
}
And you use this class like this :
$db = new DBConnection( /* your password , user , host , etc. */);
$stuff = new FooBar( $db );
$stuff->some_function();
$blah = new DifferentClass( $db );
This way you share the same connection with all the classes that require it. And now the class FooBar is not responsible for making connection or has to know your connection details.
Solution 1: Make the $db variable private, and use it via a getter.
Solution 2: Implement a singleton pattern on the dbal side.
Even when calling a static method from an instance (not recommended), you do not have
access to $this, so your class will fatal.
class sns {
public static function moo() {
var_dump($this->fng);
}
public function goo() {
var_dump($this);
}
}
sns::moo();
$_ = new sns;
$_->moo();
$_->goo();
As another poster has indicated from the php manual.
$db however can be defined statically instead so it only needs to be created once regardless of whether the object is an instance or not.
I have a variable on the global scope that is named ${SYSTEM}, where SYSTEM is a defined constant. I've got a lot of classes with functions that need to have access to this variable and I'm finding it annoying declaring global ${SYSTEM}; every single time.
I tried declaring a class variable: public ${SYSTEM} = $GLOBALS[SYSTEM]; but this results in a syntax error which is weird because I have another class that declares class variables in this manner and seems to work fine. The only thing I can think of is that the constant isn't being recognised.
I have managed to pull this off with a constructor but I'm looking for a simpler solution before resorting to that.
EDIT
The global ${SYSTEM} variable is an array with a lot of other child arrays in it. Unfortunately there doesn't seem to be a way to get around using a constructor...
Ok, hopefully I've got the gist of what you're trying to achieve
<?php
// the global array you want to access
$GLOBALS['uname'] = array('kernel-name' => 'Linux', 'kernel-release' => '2.6.27-11-generic', 'machine' => 'i686');
// the defined constant used to reference the global var
define(_SYSTEM_, 'uname');
class Foo {
// a method where you'd liked to access the global var
public function bar() {
print_r($this->{_SYSTEM_});
}
// the magic happens here using php5 overloading
public function __get($d) {
return $GLOBALS[$d];
}
}
$foo = new Foo;
$foo->bar();
?>
This is how I access things globally without global.
class exampleGetInstance
{
private static $instance;
public $value1;
public $value2;
private function initialize()
{
$this->value1 = 'test value';
$this->value2 = 'test value2';
}
public function getInstance()
{
if (!isset(self::$instance))
{
$class = __CLASS__;
self::$instance = new $class();
self::$instance->initialize();
}
return self::$instance;
}
}
$myInstance = exampleGetInstance::getInstance();
echo $myInstance->value1;
$myInstance is now a reference to the instance of exampleGetInstance class.
Fixed formatting
You could use a constructor like this:
class Myclass {
public $classvar;
function Myclass() {
$this->classvar = $GLOBALS[SYSTEM];
}
}
EDIT: Thanks for pointing out the typo, Peter!
This works for array too. If assignment is not desired, taking the reference also works:
$this->classvar =& $GLOBALS[SYSTEM];
EDIT2: The following code was used to test this method and it worked on my system:
<?php
define('MYCONST', 'varname');
$varname = array("This is varname", "and array?");
class Myclass {
public $classvar;
function Myclass() {
$this->classvar =& $GLOBALS[MYCONST];
}
function printvar() {
echo $this->classvar[0];
echo $this->classvar[1];
}
};
$myobj = new Myclass;
$myobj->printvar();
?>
The direct specification of member variables can not contain any references to other variables (class {public $membervar = $outsidevar;} is invalid as well). Use a constructor instead.
However, as you are dealing with a constant, why don't you use php's constant or class constant facilities?
You're trying to do something really out-of-the-ordinary here, so you can expect it to be awkward. Working with globals is never pleasant, especially not with your dynamic name selection using SYSTEM constant. Personally I'd recommend you use $GLOBALS[SYSTEM] everywhere instead, or ...
$sys = $GLOBALS[SYSTEM];
... if you're going to use it alot.
You could also try the singleton pattern, although to some degree it is frowned upon in OOP circles, it is commonly referred to as the global variable of classes.
<?php
class Singleton {
// object instance
private static $instance;
// The protected construct prevents instantiating the class externally. The construct can be
// empty, or it can contain additional instructions...
protected function __construct() {
...
}
// The clone and wakeup methods prevents external instantiation of copies of the Singleton class,
// thus eliminating the possibility of duplicate objects. The methods can be empty, or
// can contain additional code (most probably generating error messages in response
// to attempts to call).
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public function __wakeup() {
trigger_error('Deserializing is not allowed.', E_USER_ERROR);
}
//This method must be static, and must return an instance of the object if the object
//does not already exist.
public static function getInstance() {
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
//One or more public methods that grant access to the Singleton object, and its private
//methods and properties via accessor methods.
public function GetSystemVar() {
...
}
}
//usage
Singleton::getInstance()->GetSystemVar();
?>
This example is slightly modified from wikipedia, but you can get the idea. Try googling the singleton pattern for more information
I'd say the first two things that stand out to me are:
You don't need the brackets around the variable name, you can simply do public $system or public $SYSTEM.
While PHP may not always require it it is standard practice to encapsulate non-numeric array indexes in single or double quotes in case the string you're using becomes a constant at some point.
This should be what you're looking for
class SomeClass {
public $system = $GLOBALS['system'];
}
You can also use class constants which would instead be
class SomeClass {
const SYSTEM = $GLOBALS['system'];
}
This can be referenced within the class with 'self::SYSTEM' and externally with 'SomeClass::SYSTEM'.