I have a class that is similar to this striped down version:
abstract class MainClass extends Thread{
protected $events = [];
public function on($message, callable $callback){
$this->events[$message] = $callback;
}
}
class MyClass extends MainClass{
// has some random methods
}
I then run this which creates an instance of the class, it then runs a callback which runs the method in the passed in class.
$myClass = new MyClass();
call_user_func_array("myCallback", array($myClass));
var_dump($myClass);
function myCallback($class){
$class->on("message", function(){
// Do some stuff
});
}
When I do the var_dump on the class, the MyClass::$var array is empty. Why is that?
Edit
If I don't extend the Thread class, this works. Why is the Thread class not allowing me to edit my properties?
I was able to get this to work by using a Stackable object. What I found out, is that arrays are not thread safe, so to accomplish the task I did this:
class Events extends Stackable{
public function add($message, callable $callback){
$this[$message] = $callback;
}
}
abstract class MainClass extends Thread{
protected $events;
public function __construct(Events $events){
$this->events = $events;
}
public function on($message, callable $callback){
$this->events->add($message, $callback);
}
}
class MyClass extends MainClass{
// has some random methods
}
$evts = new Events();
$myClass = new MyClass($evts);
call_user_func_array("myCallback", array($myClass));
var_dump($myClass);
function myCallback($class){
$class->on("My Message", function(){
// Do some stuff
});
}
Related
I am testing a class in phpunit, but I am not mocking it, the class is like that:
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
return $finalValue;
}
}
in the test class I am doing like this
public function testDoSomething($param)
{
$myclass = new MyClass();
//here I need to control the value of $someValue, as it affects the final value returned
$res = $myClass->doSomething();
$this->assertEqual('sonething', res);
}
so my question is How can I control the value returned from anotherMethod method? I'd prefer to mock it so it does not call other methods in it
You could partially mock your class and instrument the methods that you do not want to test, as the following example:
public function testDoSomething()
{
/** #var \App\Models\MyClass $classUnderTest */
$classUnderTest = $this->getMockBuilder(\App\Models\MyClass::class)
->onlyMethods(['anotherMethod'])
->getMock();
$classUnderTest->expects($this->once())
->method('anotherMethod')
->willReturn('mocked-value');
$this->assertEquals("from-test mocked-value", $classUnderTest->doSomething("from-test"));
}
with the following sources:
ParentClass
class ParentClass
{
public function anotherMethod() {
return "parent-value";
}
}
and MyClass
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
$finalValue = $param . ' '. $someValue;
return $finalValue;
}
}
I have the following code and I don't know how to use the interface, I feel like I don't need it but I want to
I am using only composer for autoloading without any framework
service code (run from batch file from the example)
try {
$test = new testClass(); //how do I put here the interface , i dont want to put it here
$test->run();
}
catch(\Exception $e) {
echo $e->getMessage() . "\n";
die;
}
Test Class:
class TestClass extends AbstractClass
{
private $_repo;
public function __construct(RepositoryInterface $repo)
{
parent::__construct();
$this->_repo = $repo;
}
public function run(){
}
AbstractClass
abstract class AbstractClass
{
protected $logger;
protected $db;
public function __construct()
{
Configuration::config();
$this->db = PDOConnection::dbConnect();
}
it is not working, now I just call to the testRepository that implement the interface directly, without calling to the interface
so how can I register the interface to the constructor or I have to call it each place I initiate a testclass object
thanks
In your TestClass method construct need one argument.
public function __construct(RepositoryInterface $repo)
When you create object of class you did not use any arguments:
$test = new testClass();
Then add argument in new testClass($repo); or change __construct method like
public function __construct(RepositoryInterface $repo = null)
I think you're missing some understanding. #J.Litvak's answer does not provide this info either.
class TestClass extends AbstractClass
{
private $_repo;
public function __construct(RepositoryInterface $repo)
{
parent::__construct();
$this->_repo = $repo;
}
}
This class has a constructor which requires an object which implements the RepositoryInterface.
As such, the variable you pass must be an object, and it must have used that Interface. With me so far?
This means that the following code is incorrect.
try {
$test = new testClass();
$test->run();
} catch(\Exception $e) { ... }
The line initializing the $test variable with an instance of TestClass, must an object which uses the RepositoryInterface for the TestClass::__construct function.
So, somewhere, you must create or have:
class CustomRepository implements RepositoryInterface
{
public function findAll() { ... }
}
Now, update your try{} / catch () {} to the following:
$customRepository = new CustomerRepository();
try {
$test = new testClass($customRepository);
$test->run();
} catch(\Exception $e) { ... }
Which should work, as the CustomRepository class implements the interface you require in the TestClass::__construct parameters.
Have a look at some of the PHP docs:
interfaces
constructors and destructors
I figured it out with PHP DI module and inject it via composer
something like :
return [
// Bind an interface to an implementation
RepositoryInterface::class => object(Repository::class),
];
I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"
I've been trying to decouple my dependencies so that I can unit test functions within a class but I ran into a problem where I have a function that loops through an array of data and creates new objects based on the data. The new object does a myself INSERT with the data.
How could I write this function so I can mock the objects to creates in the loop?
public function createObjects($array_of_data)
{
$new_objects = [];
foreach($array_of_data as $data)
{
//do some stuff with the data
$new_object = new newObject($data);
$new_object->saveToDB();
$new_objects[] = $new_object;
}
return $new_objects;
}
I would suggest creating a new factory class, injecting that class into the createObjects() method (or via that class's constructor, or via a setter method), then mocking that factory when it comes time to test createObjects().
Here's a quick example. Be sure to note the FactoryInterface typehint in the YourClass::createObjects() method, which makes all of this possible:
interface FactoryInterface
{
public function createObject($data);
}
class ObjectFactory implements FactoryInterface
{
public function createObject($data)
{
return new newObject($data);
}
}
class YourClass
{
public function createObjects($array_of_data, FactoryInterface $objectFactory)
{
$new_objects = [];
foreach ($array_of_data as $data) {
$new_objects[] = $objectFactory->createObject($data);
}
return $new_objects;
}
}
class MockObjectFactory implements FactoryInterface
{
public function createObject($data)
{
// Here, you can return a mocked newObject rather than an actual newObject
}
}
class YourClassTest extends PHPUnit_Framework_TestCase
{
public function testCreateObjects()
{
$classUnderTest = new YourClass();
$new_objects = $classUnderTest->createObjects(
[1, 2, 3], // Your object initialization data.
new MockObjectFactory()
);
// Perform assertions on $new_objects
}
}
I am trying to figure out how to import a large number of PHP class functions on the fly. For example...
class Entity
{
public function __construct($type)
{
require_once $type."_functions.php"
}
// ...
}
$person = new Entity("human");
$person->sayhi();
$cow = new Entity("cow");
$cow->sayhi();
human_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Hello world!";
}
}
cow_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Moo!";
}
}
I have found a few possibilities like classkit_method_redefine() and runkit_method_redefine() (which are "experimental", and they cannot modify the currently running class anyway). I am on PHP 5.3.3 right now, so I can't use Traits (Not sure if that is what I am looking for anyways). I have had success redefining the handler variable like this:
// Example 2:
class OtherEntity { /* Code Here */ }
class Entity
{
public function __construct($type)
{
global $foo;
unset($foo);
$foo = new OtherEntity();
}
}
$foo = new Entity();
But, this feels like a very hacky method. More importantly, if I don't name every instance of the class $foo, then it will not work. Are there any workarounds for what I am trying to do?
Note: I am aware that I can extend a class, but in my case when the Entity class is initiated, there is no safe way to know in advance what subclass it would need to be initiated with. Perhaps there is a method I could write, such as:
public function changeClass
{
this->class = OtherEntity;
}
Thanks for your help!
Here's an idea of a possible solution you could try. Let the Cow and Human classes extend the Entity class. However, the Entity class would use a factory to instantiate the objects based on if the value was safe. Let's look at this in more detail:
/*
* Class Entity should not be able to be instantiated.
* It should contain a factory to instantiate the
* appropriate entity and an abstract function declaring
* the method that each entity will need to implement.
*/
abstract class Entity {
public static function factory($type) {
return (is_subclass_of($type, "Entity")) ? new $type() : FALSE;
}
abstract public function sayHi();
}
/*
* Human class extends Entity and implements the
* abstract method from Entity.
*/
class Human extends Entity {
public function sayHi() {
echo "Hello World!";
}
}
/*
* Cow class extends Entity and implements the
* abstract method from Entity.
*/
class Cow extends Entity {
public function sayHi() {
echo "Moo!";
}
}
Now to use this method, call the factory method and if all works well, it'll instantiate the proper class which will extend Entity.
$person = Entity::factory("Human");
$person->sayHi();
$cow = Entity::factory("Cow");
$cow->sayHi();
Using, is_subclass_of() will keep you safe because if the passed in value is not a class that extends Entity, you'll be returned a value of FALSE.
If you'd like to see the above code in action, copy the above php code and test it out on phpfiddle.org.
One thing you can do is create Human and Cow as subclasses of Entity. When you do new Entity("Human"), you can store a newly created Human object inside the Entity instance.
Then you can use __call to redirect method calls to the "child element".
class Entity{
private $child;
public function __construct($type){
$this->child = new $type;
}
public function __call($func, $params=array()){
$method = method_exists($this, $func)
? [$this, $func] : [$this->child, $func];
return call_user_func_array($method, $params);
}
}
class Human extends Entity{
public function __construct(){}
public function sayhi(){
echo "Hello world!";
}
}
class Cow extends Entity{
public function __construct(){}
public function sayhi(){
echo "Moo!";
}
}
$person = new Entity("Human");
$person->sayhi();
$cow = new Entity("Cow");
$cow->sayhi();
The only downside is that $person and $cow are both Entity objects.