Say I have a class Core() where it will give me the instance of different classes depending on some initialization. Say that after initiating it, I get some class and then want to instantiate that. This is what I do:
$core = new Core();
// $core is further initiated
$api = $core->getClass(); // This returns, for instance class Library_MyClass
$class = new $api();
Is there a way to combine the last two steps into one? So for instance I say something like $class = new $core->getClass()()? Obviously what I wrote is wrong, but that is sort of what I want! Is that possible?
If this is some form of a factory you could do something like:
class Core
{
public function getClass()
{
return new Library_MyClass();
}
}
$core = new Core();
$class = $core->getClass();
However considering the name of the class Core I suspect you may be violating some SOLID principles here.
You might want to benefit from the use of dependency injection (considering a slight update applied to PeeHaa example)
class Core
{
public function getClass($api)
{
return new $api();
}
}
$core = new Core();
$apiInstance = $core->getClass('Library_MyClass');
This way you won't have to update the Core class whenever you would like it to provide you with an instance of another library class.
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
Been searching all around but still cannot find a solution for this problem.
My problem is that i got these snips of code(Examples):
Core file
class Core {
public $DB = null;
public $Handler = null;
function run() {
$this->DB = "somedatabase";
include_once('handler.php');
$this->Handler = new Handler;
$this->Handler->run();
}
}
This is the helper.php example
class Handler extends Core {
function run() {
echo "<pre>"; print_r($this); echo "</pre>"; die();
}
}
Even tho i defined the DB variable before i include the helper then it is still empty inside the helper class. It's defined yes but it's empty. Which means it properly doesn't share the same memory as the Core class.
Keep in mind that the Core class it self is instanced too.
-
Thanks for all suggestions
Edit
PhpMyCoder got it right. Thank you for the detailed and well written reply.
For over 2 years i been seeing PHP scopes as being the same or sorta the same as JavaScript's scope. Now i realize that if i extend my "Core" class i get all the methods and properties within it. But the values is private to my class and my class alone.
This is great. Finally i got it.
From what I gather here you are talking about public instance variables. They are performing as OOP would require. Each time you instantiate a class with
$core = new Core(); // or
$handler = new Handler();
Each of them gets a fresh space in memory to store their instance variables. Instance variables are unique to each instance of a class, as the name would suggest. So, two separate instances of Core and Handler do not share instance variables. However since Handler extends Core, two instances of Core are created. One instance is the one that I created on the first line. The other is created so that Handler can extend it on the second line. These two instances of Core are not the same object. To have the same values for Core across all core objects you will need to use static (class) variables.
class Core {
public static $hello = 'World';
}
var_dump(Core::$hello); //string('Word')
In my example, $hello will always be available to everyone by accessing it with the scope resolution operator, ::. So Handler could access it with either Core::$hello or parent::$hello. If you wanted to only expose this static variable to Core and its subclasses, then you would need to make it protected and access it from within Core with self::$hello and from its subclasses with parent::$hello.
class Core {
protected static $hello = 'World';
public function sayHello() {
echo 'Hello '.self::$hello; //from within Core, access with `self`
}
}
class Handler extends Core {
public function myParentSays() {
echo 'My parent says: Hello '.parent::$hello;
}
}
$core = new Core();
$core->sayHello(); // 'Hello World'
$handler = new Handler();
$handler->myParentSays(); // 'My parent says: Hello World'
Check the PHP docs for more on the static keyword and the scope resolution operator.
EDIT
I believe your confusion lies in a misunderstanding of how inheritance works in OOP so let me give you a little real-world-ish example. Let's say you create a class for employees called Employee. This class has a public instance variable (that is, one that can be accessed with ->) for the name of the person. In PHP this would be:
class Employee {
public $name;
public __construct($name) {
$this->name = $name;
}
}
Now let's create a new employee:
$tim = new Employee('Tim');
Let's say that we need a new class, Intern, that should subclass Employee. That should be easy enough:
class Intern extends Employee {
public function makeCoffee(Employee $receiver) {}
}
If we create a new intern now, should his name be Time just because we have already created another employee named Tim? No. That doesn't make sense.
$intern = new Intern();
var_dump($intern->name); //string(0) ""
Now say that setting the name was some complicated and arduous process and we'd rather not have to code it again. With a little modification to our Intern class we can leave the name setting to its superclass, Employee.
class Intern {
public function __construct($name) {
parent::__construct($name);
}
public function makeCoffee(Employee $receiver) {}
}
Now we can create a new intern and set his or her name. Notice how the other Employee keeps his name.
$intern = new Intern('Something Forgettable');
var_dump($intern->name); // string(21) "Something Forgettable"
var_dump($employee->name); // string(3) "Tim"
Now why is this? In OOP, a subclass/superclass is an "is a" relationship. The Intern "is an" Employee. The Intern has all the same properties and methods as an Employee but because each Intern and Employee are distinct they have their own values for these properties.
With this in mind, I suggest you rethink your strategy for your classes. Does it really make sense that Handler is a Core? Does it make sense that MainController is a Handler?
Classes don't share memory unless you pass a reference to them. When you make an instance of a class (an object), it is unique. You could have:
$a = new Core();
$b = new Core();
$a->var1 = 'foo';
$b->var1 = 'bar';
echo $a->var1; // 'foo'
echo $b->var1; // 'bar'
The same holds for extending a class. It doesn't explicitly share the values of the fields, it just shares their existence/visibility.
To share the value, you would do something more like this:
$a = new Core();
$b = new Core();
$c = 'foo';
$a->var1 = &$c;
$b->var1 = &$c;
echo $a->var1; // 'foo'
$b->var1 = 'bar';
echo $a->var1; // 'bar'
$c = 'baz';
echo $a->var1; // 'baz'
Variables are only set on objects (class instances). Don't confuse classes with objects.
If you want to have variables bound to classes, use the static keyword:
class Core {
public static $static = 'abc';
public $instance = 'xyz';
}
Core::$static = 'x';
$core = new Core();
$core->instance = 'a';
In PHP, classes are extended, not objects. This is class:
class SomeClass{
// ...
}
And this is object:
$object = new SomeClass();
So, when your are extending some class, all its protected/public properties are become available to child class.
If I declared a class in a controller and want to use it in a model without passing the class' pointer, how can I redeclare that class without the "Fatal error: Class already declared"? If I use the get_declared_classes() function, I see that the class is declared, but how can I get the pointer to that class so that I can use it in the model?
Basically, how can I use a class that's been declared but with no pointer.
Any help would be greatly appreciated.
Thanks in advance!
EDIT: Maybe the word "pointer" was misused. Here's some code
// Controller...one file
$class = new Class();
$model = $this->load_model('example.php');
$model->dosomething();
// Model...example.php
function dosomething() {
// I want to access the class here. Is it only possible to do this by
// passing a $class parameter to the function or can I do it without
// passing it as a variable?
}
I think you're mixing terminology. There's no concept of a pointer anywhere in PHP. References are similar concepts, but that's another topic.
What I think you're trying to do, is use a variable to indicate the class in the model. So, you can use a string. So let's say you want to tell the model to use class Foo, you could inject the class name into the model:
$model = new Model('foo');
Then, inside the constructor:
public function __construct($class) {
$this->className = $class;
}
Then, when you want to use it, just call new:
$class = $this->className;
$obj = new $class();
But note that it has nothing to do with object scope. So you could do it anywhere:
$class = 'Foo';
$obj = new $class;
public function getHelperInstance()
{
$user = new Helper();
$user->set($result['data']);
return $user;
}
I am calling getHelper() class multiple times and if $user is not empty than am calling getHelperInstance(), now in my case getHelperInstance() always creates a new instance of Helper() class and so every time I call getHelperInstance() function am creating a new instance of Helper() so is there any way where can I can just create one instance of Helper() and use it multiple times instead of creating a new instance everytime. Any suggestions !!!
public function getHelper()
{
$user = array();
if (!empty($user))
{
$user = $this->getHelperInstance();
}
return $user;
}
Here is what Erich Gamma, one of the Singleton pattern's inventors, has to say about it:
"I'm in favor of dropping Singleton. Its use is almost always a design smell"
So, instead of a Singleton, I suggest to use Dependency Injection.
Create the Helper instance before you create what is $this. Then set the helper instance to the $this instance from the outside, either through a setter method or through the constructor.
As an alternative, create a Helper broker that knows how to instantiate helpers by name and pass that to the $this instance:
class HelperBroker
{
protected $helpers = array();
public function getHelper($name)
{
// check if we have a helper of this name already
if(!array_key_exists($name, $this->helpers)) {
// create helper and store for later subsequent calls
$this->helpers[$name] = new $name;
}
return $this->helpers[$name];
}
}
This way you can lazy load helpers as needed and will never get a second instance, without having to use Singleton. Pass an instance of the broker to every class that needs to use helpers.
Example with a single helper
$helper = new Helper;
$someClass = new Something($helper);
and
class Something
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function useHelper()
{
$return = $this->helper->doSomethingHelpful();
}
}
Inside $something you can now store and access the helper instance directly. You don't need to instantiate anything. In fact, $something doesn't even have to bother about how a helper is instantiated, because we give $something everything it might need upfront.
Now, if you want to use more than one helper in $someClass, you'd use the same principle:
$helper1 = new Helper;
$helper2 = new OtherHelper;
$something = new Something($helper1, $helper2);
This list will get rather long the more dependencies you insert upfront. We might not want to instantiate all helpers all the time as well. That's where the HelperBroker comes into play. Instead of passing every helper as a ready instance to the $something, we inject an object that knows how to create helpers and also keeps track of them.
$broker = new HelperBroker;
$something = new Something($broker);
and
class Something
{
protected $helperBroker;
public function __construct($broker)
{
$this->helperBroker = $broker;
}
public function doSomethingHelpful()
{
$return = $this->getHelper('foo')->doSomethingHelpful();
}
public function doSomethingElse()
{
$return = $this->getHelper('bar')->doSomethingElse();
}
}
Now $something can get the helpers it needs, when it needs them from the broker. In addition, any class that needs to access helpers does now no longer need to bother about how to create the helper, because this logic is encapsulated inside the broker.
$broker = new HelperBroker;
$something = new Something($broker);
$other = new Other($broker);
The broker also makes sure that you only have one helper instance, because when a helper was instantiated, it is stored inside the broker and returned on subsequent calls. This solves your initial problem, that you don't want to reinstance any helpers. It also doesn't force your helpers to know anything about how to manage themselves in the global state, like the Singleton does. Instead you helpers can concentrate on their responsibility: helping. That's clean, simple and reusable.
It sounds like you are interested in the singleton pattern. If you are using PHP5+, you should be able to take advantage of PHP's OOP stuff.
Here's an article on how to implement a singleton in php4. (But I would strongly suggest updating to php5 if that is an option at all)
class Singleton {
function Singleton() {
// Perform object initialization here.
}
function &getInstance() {
static $instance = null;
if (null === $instance) {
$instance = new Singleton();
}
return $instance;
}
}
PHP 4 Singleton Pattern
FYI, if you have any control over which PHP version you use you really should migrate to PHP 5.
I've searched but can't quite find what I'm looking for and the manual isn't much help in this respect. I'm fairly new to unit testing, so not sure if I'm on the right track at all. Anyway, onto the question. I have a class:
<?php
class testClass {
public function doSomething($array_of_stuff) {
return AnotherClass::returnRandomElement($array_of_stuff);
}
}
?>
Now, clearly I want the AnotherClass::returnRandomElement($array_of_stuff); to return the same thing every time. My question is, in my unit test, how do I mockup this object?
I've tried adding the AnotherClass to the top of the test file, but when I want to test AnotherClass I get the "Cannot redeclare class" error.
I think I understand factory classes, but I'm not sure how I would apply that in this instance. Would I need to write an entirely seperate AnotherClass class which contained test data and then use the Factory class to load that instead of the real AnotherClass? Or is using the Factory pattern just a red herring.
I tried this:
$RedirectUtils_stub = $this->getMockForAbstractClass('RedirectUtils');
$o1 = new stdClass();
$o1->id = 2;
$o1->test_id = 2;
$o1->weight = 60;
$o1->data = "http://www.google.com/?ffdfd=fdfdfdfd?route=1";
$RedirectUtils_stub->expects($this->any())
->method('chooseRandomRoot')
->will($this->returnValue($o1));
$RedirectUtils_stub->expects($this->any())
->method('decodeQueryString')
->will($this->returnValue(array()));
in the setUp() function, but these stubs are ignored and I can't work out whether it's something I'm doing wrong, or the way I'm accessing the AnotherClass methods.
Help! This is driving me nuts.
With Unit Tests you want to create 'test' classes that contain static data, and then pass those into your tested class. This removes variables from the testing.
class Factory{
function build()
{
$reader = new reader();
$test = new test($reader);
// ..... do stuff
}
}
class Factory{
function build()
{
$reader = new reader_mock();
$test = new test($reader);
// ..... do stuff
}
}
class reader_mock
{
function doStuff()
{
return true;
}
}
Because you are using Static Classes, you would have to remove AnotherClass from the program, and then recreate it so that it only contains functions that return test data. Normally though, you, don't want to actually remove classes from the program, which is why you pass classes in like the above example.