Is it possible to make all function's vars global without typing all of them like global $a, $b, $c...?
Try creating a Static object within your application and assigning variables to that scope, Like so!
<?php
/*
* Core class thats used to store objects of ease of access within difficult scopes
*/
class Registry
{
/*
* #var array Holds all the main objects in an array a greater scope access
* #access private
*/
private static $objects = array();
/**
* Add's an object into the the global
* #param string $name
* #param string $object
* #return bool
*/
public static function add($name,$object)
{
self::$objects[$name] = $object;
return true;
}
/*
* Get's an object out of the registry
* #param string $name
* #return object on success, false on failure
*/
public static function get($name)
{ if(isset(self::$objects[$name]))
{
return self::$objects[$name];
}
return false;
}
/**
* Removes an object out of Registry (Hardly used)
* #param string $name
* #return bool
*/
static function remove($name)
{
unset(self::$objects[$name]);
return true;
}
/**
* Checks if an object is stored within the registry
* #param string $name
* #return bool
*/
static function is_set($name)
{
return isset(self::$objects[$name]);
}
}
?>
Considering this file is one of the first files included you can set any object/array/variable/resource/ etc into this scope.
So lets say you have just made a DB Connection, this is hwo you use it
...
$database = new PDO($dns);
Registry::add('Database',$database);
$DBConfig = Registry::get('Database')->query('SELECT * FROM config_table')->fetchAll(PDO::FETCH_CLASS);
Registry::add('Config',$DBConfig);
No anywhere else within your script you can add or retrieve items.
with Registry::get('ITEM_NEEDED');
This will work in methods functions etc.
Perfect example
function insertItem($keys,$values)
{
Registry::get('Database')->query('INSERT INTO items ('.implode(',',$keys).') VALUES ('.implode(',',$values).')');
}
Hope it helps
No. That'd be an awful thing to do anyways.
You can pass them as arguments and then you won't need the global keyword:
function(&$a, &$b, &$c)
{
// your code........
}
You can always use $GLOBALS["var"] instead of $var. Of course, if you need this, you're most likely doing it wrong.
Related
There are many methods around that allow you to access private variables of another class, however what is the most efficient way?
For example:
I have this class with some details in:
class something{
private $details =
['password' => 'stackoverflow',];
}
In another class I need to access them, example (although this obviously wouldn't work since the variable isn't in scope to this class):
class accessSomething{
public function getSomething(){
print($something->details['password']);
}
}
Would a function like this be good enough within the class "something" to be used by the access class?:
public function getSomething($x, $y){
print $this->$x['$y'];
}
you should be using proper getters/setters in your classes to allow access to otherwise restricted data.
for example
A class
class AClass {
private $member_1;
private $member_2;
/**
* #return mixed
*/
public function getMember1() {
return $this->member_1;
}
/**
* #param mixed $member_1
*/
public function setMember1( $member_1 ) {
$this->member_1 = $member_1;
}
/**
* #return mixed
*/
public function getMember2() {
return $this->member_2;
}
/**
* #param mixed $member_2
*/
public function setMember2( $member_2 ) {
$this->member_2 = $member_2;
}
}
which is then called as follows:
$newClass = new AClass();
echo $newClass->getMember1();
echo $newClass->getMember2();
I have the following class
namespace PG\Referrer\Single\Post;
class Referrer implements ReferrerInterface
{
/**
* #var $authorReferrer = null
*/
protected $isAuthorReferrer = null;
/**
* #var $dateReferrer = null
*/
protected $isDateReferrer = null;
/**
* #var $searchReferrer = null
*/
protected $isSearchReferrer = null;
/**
* #var $taxReferrer = null
*/
protected $isTaxReferrer = null;
/**
* #param array $values = null;
*/
public function __construct(array $values = null)
{
if ($values)
$this->setBulk($values);
}
/**
* Bulk setter Let you set the variables via array or object
*/
public function setBulk($values)
{
if (!is_array($values) && !$values instanceof \stdClass) {
throw new \InvalidArgumentException(
sprintf(
'%s needs either an array, or an instance of \\stdClass to be passed, instead saw %s',
__METHOD__,
is_object($values) ? get_class($values) : gettype($values)
)
);
}
foreach ($values as $name => $value) {//create setter from $name
global $wp_query;
if (array_key_exists($value, $wp_query->query_vars)) { //Check that user don't set a reserved query vars
throw new \InvalidArgumentException(
sprintf(
'%s is a reserved query_vars and cannot be used. Please use a unique value',
$value
)
);
}
$setter = 'set' . $name;
$condition = isset($_GET[$value]);
if ($setter !== 'setBulk' && method_exists($this, $setter)) {
$this->{$setter}($condition);//set value (bool)
}
}
return $this;
}
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer)
{
$this->isAuthorReferrer = $isAuthorReferrer;
return $this;
}
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer)
{
$this->isDateReferrer = $isDateReferrer;
return $this;
}
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer)
{
$this->isSearchReferrer = $isSearchReferrer;
return $this;
}
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer)
{
$this->isTaxReferrer = $isTaxReferrer;
return $this;
}
}
with its interface
namespace PG\Referrer\Single\Post;
interface ReferrerInterface
{
/**
* #param array $values
* #return $this
*/
public function setBulk($values);
/**
* #param bool $authorReferrer
* #return $this
*/
public function setAuthorReferrer($isAuthorReferrer);
/**
* #param bool $dateReferrer
* #return $this
*/
public function setDateReferrer($isDateReferrer);
/**
* #param bool $searchReferrer
* #return $this
*/
public function isSearchReferrer($isSearchReferrer);
/**
* #param bool $taxReferrer
* #return $this
*/
public function setTaxReferrer($isTaxReferrer);
}
This class sets up 4 conditionals that I need to use in another class. The values that is used in this class is also set from the other class, so basically the user sets values in the other class (lets call it class b) that is then used by class Referrer and returns the 4 conditionals which is then used by class b.
The reason why I'm doing it this way is because there will be two other classes that will need to do the same, but will returns different info
What is the more correct way to achieve this?
EDIT
To clear this up
class Referrer
The properties $isAuthorReferrer, $isDateReferreretc will either have a value of null or a boolean value depending on what is set by the user.
Example:
$q = new Referrer(['authorReferrer' => 'aq']);
In the code above, $isAuthorReferrer is set via the setBulk() method in the class to true when the variable aq is available in the URL or false when not present. The three other properties will return null because they are not set in the example.
The above works as expected, but I need to do this in another class, lets again call it class b. The arguments will be set to class b, and in turn, class b will set this arguments to class Referrer, class Referrer will use this arguments and return the proper values of its properties, and class b will use this results to do something else
Example:
$q = new b(['authorReferrer' => 'aq']);
Where class b could be something like this (it is this part that I'm not sure how to code)
class b implements bInterface
{
protected $w;
protected $other;
public function __construct($args = [])
{
//Do something here
// Do something here so that we can use $other in other classes or functions
}
public function a()
{
$w = new Referrer($args);
}
public function b()
{
// use $w properties here
// return $other for usage in other classes and functions
}
}
The best way is to inject the referrer to your classes in order to do loose coupling between them and the referrer (this pattern use the benefit of your ReferrerInterface):
class b implements bInterface
{
protected $referrer;
public function __construct(ReferrerInterface $referrer, array $values = array())
{
$this->referrer = $referrer;
$this->referrer->setBulk($values);
}
public function getReferrer()
{
return $this->referrer;
}
public function b()
{
// use $this->referrer properties here
}
}
// Instantiation (use your dependency injection if you have one):
$referrer = new Referrer();
$b = new b($referrer, ['authorReferrer' => 'aq']);
I do not understand what is $other so I removed it but explain me if you want me to I add it again.
If you need to use the properties of the referrer in b, you should add some getters in your ReferrerInterface to allow that. I would use setAuthorReferrer($isAuthorReferrer) to set the value and isAuthorReferrer() to get it for instance.
I'm a little bit confused about how to unit test a constructor, particularly since it returns no value.
Let's assume I have this class:
class MyClass {
/** #var array */
public $registered_items;
/**
* Register all of the items upon instantiation
*
* #param array $myArrayOfItems an array of objects
*/
public function __construct($myArrayOfItems) {
foreach($myArrayOfItems as $myItem) {
$this->registerItem($myItem);
}
}
/**
* Register a single item
*
* #param object $item a single item with properties 'slug' and 'data'
*/
private function registerItem($item) {
$this->registered_items[$item->slug] = $item->data;
}
}
Obviously this is a bit contrived and incredibly simple, but it's for the sake of the question. =)
So yeah, how would I go about writing a unit test for the constructor here?
Bonus question: am I right in thinking that no unit test for registerItem() would be needed in a case such as this?
EDIT
How about if I re-factored to remove the logic from the constructor. How would I test registerItem() in this case?
class MyClass {
/** #var array */
public $registered_items;
public function __construct() {
// Nothing at the moment
}
/**
* Register all of the items
*
* #param array $myArrayOfItems an array of objects
*/
public function registerItem($myArrayOfItems) {
foreach($myArrayOfItems as $item) {
$this->registered_items[$item->slug] = $item->data;
}
}
}
Add a method to look up a registered item.
class MyClass {
...
/**
* Returns a registered item
*
* #param string $slug unique slug of the item to retrieve
* #return object the matching registered item or null
*/
public function getRegisteredItem($slug) {
return isset($this->registered_items[$slug]) ? $this->registered_items[$slug] : null;
}
}
Then check that each item passed to the constructor in the test has been registered.
class MyClassTest {
public function testConstructorRegistersItems() {
$item = new Item('slug');
$fixture = new MyClass(array($item));
assertThat($fixture->getRegisteredItem('slug'), identicalTo($item));
}
}
Note: I'm using the Hamcrest assertions, but PHPUnit should have an equivalent.
For First Code
public function testConstruct{
$arrayOfItems = your array;
$myClass = new MyClass($arrayOfItems);
foreach($arrayOfItems as $myItem) {
$expected_registered_items[$item->slug] = $item->data;
}
$this->assertEquals($expected_registered_items, $myClass->registered_items);
}
In my php application I have been comparing objects with the usual equality comparison operator, e.g.:
if ($objectA == $objectB) { ... }
Recently I implemented proxies (for objects which are expensive to load) however this means the equality operator no longer works. Is there a simple way around this? One that doesn't rely on reflection?
For the moment, I have resorted to testing the unique identifier of each object, e.g.
if ($objectA->getId() == $objectB->getId) { ... }
But this has two problems: 1) I need to refactor all existing code, and 2) in the future I may need to compare objects which are value objects (not entities).
I'm not hopeful of an easy solution since I think it would require a new magic method...
Here's my AbstractProxy class. Any help appreciated...
abstract class KOOP_Base_AbstractProxy
implements KOOP_Base_iDomain
{
use KOOP_Trait_Helper_Helper;
/**
* #var integer Object identifier
*/
protected $_id = null;
/**
* #var KOOP_Base_AbstractMapper
*/
protected $_mapper = null;
/**
* #var KOOP_Base_AbstractDomain Actual object
*/
protected $_subject = null;
/**
* Store object id for lazy loading
*
* #param integer $id Object identifier
* #param string $mapper Mapper by which to retrieve object
*/
public function __construct($id, $mapper)
{
$this->_id = $id;
$this->_mapper = $mapper;
}
/**
* Get subject
*
* #return KOOP_Base_AbstractDomain
*/
protected function getSubject()
{
if (!$this->_subject) {
$this->_subject = $this->getMapper($this->_mapper)->find($this->_id);
}
return $this->_subject;
}
/**
* Get property
*
* #param string $property
* #return mixed
*/
public function __get($property)
{
return $this->getSubject()->$property;
}
/**
* Set property
*
* #param string $property
* #param mixed $value
* #return void
*/
public function __set($property, $value)
{
$this->getSubject()->$property = $value;
}
/**
* Is property set?
*
* #param $property
* #return boolean
*/
public function __isset($property)
{
return isset($this->getSubject()->$property);
}
/**
* Unset property
*
* #param string $property
* #return mixed
*/
public function __unset($property)
{
unset($this->getSubject()->$property);
}
/**
* Call method
*
* #param string $method Method to call
* #param array $params Parameters to pass
* #return mixed
*/
public function __call($method, array $params)
{
return call_user_func_array(array($this->getSubject(), $method), $params);
}
/**
* Get id
*
* Saves having to retrieve the entire object when only the ID is required.
*/
public function getId()
{
return $this->_id;
}
}
Proxies do break object equality, and there's no utterly clean way to fix this. In a fully object oriented language you would handle this by operator overloading (which I don't recommend) or implementing a custom .equals() function (as in Java). Sadly, PHP simply does not support object orientation at this level, so you will have some decisions to make.
1) I would prefer to have your proxy class provide an equals() function which takes as input a reference to the object you want to test against and compares it to the proxied object - which shouldn't be much more 'expensive' than it was to not use a proxy at all. Example in pseudo-PHP code (my apologies if my reference syntax is off, it's been a while):
public function equals (&$toCompare)
{
if ($_subject == $toCompare)
{
return true;
}
else
{
return false;
}
}
The downside is simple: you have to refactor your code that involves this proxied object, and you have to remember that "==" does not work on this proxied object type while you are working. If you don't deal with these objects much, or if you deal with them all the time, this is fine. If you deal with them regularly but intermittently, or if others must work with them on occasion, then this will cause bugs when you/they forget about this equality problem.
2) Use an Operator Overloading extension to the language. I haven't done this, I don't know if it works, and it might be a nightmare. I include it for theoretical completeness.
Personally, I think I'd just hack it with the pseudo-Java approach call it a day, as I think it would actually work and require nothing more than using the function correctly (and remembering to use it in the first place).
Does PHP have a method of having auto-generated class variables? I think I've seen something like this before but I'm not certain.
public class TestClass {
private $data = array();
public function TestClass() {
$this->data['firstValue'] = "cheese";
}
}
The $this->data array is always an associative array but they keys change from class to class. Is there any viable way to access $this->data['firstValue'] from $this->firstValue without having to define the link?
And if it is, are there any downsides to it?
Or is there a static method of defining the link in a way which won't explode if the $this->data array doesn't contain that key?
See here: http://www.php.net/manual/en/language.oop5.overloading.php
What you want is the "__get" method. There is an example for what you need on the link.
Use the PHP5 "magic" __get() method. It would work like so:
public class TestClass {
private $data = array();
// Since you're using PHP5, you should be using PHP5 style constructors.
public function __construct() {
$this->data['firstValue'] = "cheese";
}
/**
* This is the magic get function. Any class variable you try to access from
* outside the class that is not public will go through this method. The variable
* name will be passed in to the $param parameter. For this example, all
* will be retrieved from the private $data array. If the variable doesn't exist
* in the array, then the method will return null.
*
* #param string $param Class variable name
*
* #return mixed
*/
public function __get($param) {
if (isset($this->data[$param])) {
return $this->data[$param];
} else {
return null;
}
}
/**
* This is the "magic" isset method. It is very important to implement this
* method when using __get to change or retrieve data members from private or
* protected members. If it is not implemented, code that checks to see if a
* particular variable has been set will fail even though you'll be able to
* retrieve a value for that variable.
*
* #param string $param Variable name to check
*
* #return boolean
*/
public function __isset($param) {
return isset($this->data[$param]);
}
/**
* This method is required if you want to be able to set variables from outside
* your class without providing explicit setter options. Similar to accessing
* a variable using $foo = $object->firstValue, this method allows you to set
* the value of a variable (any variable in this case, but it can be limited
* by modifying this method) by doing something like:
* $this->secondValue = 'foo';
*
* #param string $param Class variable name to set
* #param mixed $value Value to set
*
* #return null
*/
public function __set($param, $value) {
$this->data[$param] = $value;
}
}
Using the magic __get, __set, and __isset constructors will allow you to control how you want variables to be set on a class while still storing all the values in a single array.
Hope this helps :)