I am trying to learn to the Dependency Inversion Principle. Currently my code is like this
class Example {
public function __construct( $input, $output ) {
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output );
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example( $input, $output)
However, it seems using basic dependency injection, it should be more like this?
class Example {
public function __construct( $input_handler, $output_handler ) {
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$input_handler = new InputHandler( $input );
$output_handler = new OutputHandler( $output);
$example = new Example( $input_handler, $output_handler)
Is this is correct?
I want to let the programmer choose the type of input / output to use when running the program. So with dependency injection (as far as I understand) it would look like this;
$input = new ConsoleInput();
$output = new FileOutput();
$input_handler = new ConsoleInputHandler( $input );
$output_handler = new FileOutputHandler( $output);
$example = new Example( $input_handler, $output_handler);
$example->doStuffToOutput();
However, I would prefer to make the programmers life a little easier by only needing to pass in the type of input and output, and not need to worry about the classes handling them;
$input = new ConsoleInput();
$output = new FileOutput();
$example = new Example( $input, $output );
$example->doStuffToOutput();
or even
$example = new Example( new ConsoleInput(), new FileOutput() );
$example->doStuffToOutput();
How can I achieve this using DIP and not end up with my initial code block? Is this a good thing to do?
While I was reading your question I felt you have two main goals. Firstly to improve the readability of your code ('..ease the programmer's life') and secondly to decouple "Example" class from the I/O handlers. For my point of view, DI is just a principle to follow in order to reach your goals.
Before I'm attaching any code, I want to emphasize that sometimes it is better to actually couple your code. Code must be coupled somehow. Do not use DI everywhere just because it has been said. Simplicity, as being described with the KISS and YAGNI principles, is always the winner.
So the big question here is whether your second goal (decoupling with DI) is the smart thing to do. Is there a real reason for the InputHandler / OutputHandler in the "Exmaple" class to be changed? If "No" is your answer, I would recommend you to keep it inside this class intact. And "maybe in the distant future it will be profitable" doesn't really count.
However, if your handlers should be unique for each type (file, console etc.), and your decoupling would help you and other programmers to extend the platform, you can take advantage of the Factory pattern. You have several ways to implement this pattern (static / abstract / simple / method factories). The main goal is to lessen the learning curve from the client, and make the "Example" class decoupled, so that adding more types or handlers would not affect this class.
class HandlerFactory {
protected static function createInputHandler(Input $input)
{
switch ($input)
{
case is_a($input, 'FileInput'):
return new FileInputHandler($input);
case is_a($input, 'ConsoleInput'):
return new ConsoleInputHandler($input);
}
throw new \Exception('Missing Input handler');
}
protected static function createOutputHandler(Output $output)
{
switch ($output)
{
case is_a($output, 'FileOutput'):
return new FileOutputHandler($output);
case is_a($output, 'ConsoleOutput'):
return new ConsoleOutputHandler($output);
}
throw new \Exception('Missing Output handler');
}
public static function createHandler($io)
{
switch ($io)
{
case is_a($io, 'Input'):
return self::createInputHandler($io);
case is_a($io, 'Output'):
return self::createOutputHandler($io);
}
throw new \Exception('Missing I/O handler');
}
}
Now your first code in your question is still relevant with a minor twist:
class Example {
public function __construct($input, $output) {
$input_handler = HandlerFactory::createHandler($input);
$output_handler = HandlerFactory::createHandler($output);
$input_handler->doStuff();
$output_handler->doOtherStuff();
}
}
$input = new Input();
$output = new Output();
$example = new Example($input, $output);
Use an Abstract Factory class to handle the instantiation of objects needed to handle i/o. You could either inject the factory into the example class, or let the factory instantiate the objects needed and then inject those into the example class. Then you can do:
$IOFactory = new IOFactory();
$example = new Example($IOFactory::makeInputHandler($inputType), $IOFactory::makeOutputHandler($outputType));
$example->doStuffToOutput();
IOFactory takes care of instantiating input and output objects base in their specific types, and then instantiates the handlers and inject them with the input and output object. Afterwards returns the handler objects to be injected in the example object.
In your case you can choose one of many creational design patterns available. My suggestion is to go with either Factory pattern or object pool pattern.
In case of factory method pattern, you can have a class with the responsibility of creating object:
class ObjectFactory {
public InputHandler createInputHandlerObject(inputobj){
if( inputobj instanceOf ConsoleInput ) {
return new ConsoleInputHandler();
} else if( inputobj instanceOf FileInput ) {
}
}
// similarly create a method for creating OutputHandler object.
//create the appropriate object by using instanceOf operator.
Since I'm familiar with Java i have given example in Java . you can change the syntax and use accordingly. This is not the only way to implement Factory Pattern.
If you want to remove the burden of creating the object at runtime you can use Object pool pattern. Hybrid of prototype pattern also becomes handy in your csse.
Related
Im trying to figure out how to call functions based on what a user clicks on a form. But im not sure if im doing it right.
I have a number of classes, lets say 3 for different ways to connect to a site, the user clicks on which one they would like.
FTP
SFTP
SSH
Which i have named 'service' in my code.
I don't want to run a whole bunch of IF statements, i would rather try and build the call dynamically.
What i have at the moment is as follows
$ftp_backup = new FTPBackup;
$sftp_backup = new SFTPBackup;
$ssh_backup = new SSHBackup;
$service = $request->input('service') . '_backup';
$service->testConn($request);
Im getting the following error
Call to a member function testConn() on string
Im not sure im doing this right.
Any help would be greatly appreciated.
Thanks
First of all $service is a string on which You cannot call method, because it is not an object (class instance).
I think it is a great example of where You can use Strategy Pattern which look like that:
class BackupStrategy {
private $strategy = null;
public function __construct($service_name)
{
switch ($service_name) {
case "ftp":
$this->strategy = new FTPBackup();
break;
case "sftp":
$this->strategy = new SFTPBackup();
break;
case "ssh":
$this->strategy = new SSHBackup();
break;
}
}
public function testConn()
{
return $this->strategy->testConn();
}
}
And then in place where You want to call it You call it by:
$service = new BackupStrategy($request->input('service'));
$service->testConn($request);
I suggest You to read about Design Patterns in OOP - it will help You a lot in the future.
How about this:
$ftp_backup = new FTPBackup;
$sftp_backup = new SFTPBackup;
$ssh_backup = new SSHBackup;
$service = $request->input('service') . '_backup';
${$service}->testConn($request);
This is called "Variables variable": http://php.net/manual/en/language.variables.variable.php
// Create class name
$className = $request->get('service') . '_backup';
// Create class instance
$service = new $className();
// Use it as you want
$service->testConn($request);
How do I get started with mocking a web service in PHP? I'm currently directly querying the web API's in my unit testing class but it takes too long. Someone told me that you should just mock the service. But how do I go about that? I'm currently using PHPUnit.
What I have in mind is to simply save a static result (json or xml file) somewhere in the file system and write a class which reads from that file. Is that how mocking works? Can you point me out to resources which could help me with this. Is PHPUnit enough or do I need other tools? If PHPUnit is enough what part of PHPUnit do I need to check out? Thanks in advance!
You would mock the web service and then test what is returned. The hard coded data you are expecting back is correct, you set the Mock to return it, so then additional methods of your class may continue to work with the results. You may need Dependency Injection as well to help with the testing.
class WebService {
private $svc;
// Constructor Injection, pass the WebService object here
public function __construct($Service = NULL)
{
if(! is_null($Service) )
{
if($Service instanceof WebService)
{
$this->SetIWebService($Service);
}
}
}
function SetWebService(WebService $Service)
{
$this->svc = $Service
}
function DoWeb($Request)
{
$svc = $this->svc;
$Result = $svc->getResult($Request);
if ($Result->success == false)
$Result->Error = $this->GetErrorCode($Result->errorCode);
}
function GetErrorCode($errorCode) {
// do stuff
}
}
Test:
class WebServiceTest extends PHPUnit_Framework_TestCase
{
// Simple test for GetErrorCode to work Properly
public function testGetErrorCode()
{
$TestClass = new WebService();
$this->assertEquals('One', $TestClass->GetErrorCode(1));
$this->assertEquals('Two', $TestClass->GetErrorCode(2));
}
// Could also use dataProvider to send different returnValues, and then check with Asserts.
public function testDoWebSericeCall()
{
// Create a mock for the WebService class,
// only mock the getResult() method.
$MockService = $this->getMock('WebService', array('getResult'));
// Set up the expectation for the getResult() method
$MockService->expects($this->any())
->method('getResult')
->will($this->returnValue(1)); // Change returnValue to your hard coded results
// Create Test Object - Pass our Mock as the service
$TestClass = new WebService($MockService);
// Or
// $TestClass = new WebService();
// $TestClass->SetWebServices($MockService);
// Test DoWeb
$WebString = 'Some String since we did not specify it to the Mock'; // Could be checked with the Mock functions
$this->assertEquals('One', $TestClass->DoWeb($WebString));
}
}
This mock may then be used in the other functions since the return is hard coded, your normal code would process the results and perform what work the code should (Format for display, etc...). This could also then have tests written for it.
How can I check if an object will be successfully instantiated with the given argument, without actually creating the instance?
Actually I'm only checking (didn't tested this code, but should work fine...) the number of required parameters, ignoring types:
// Filter definition and arguments as per configuration
$filter = $container->getDefinition($serviceId);
$args = $activeFilters[$filterName];
// Check number of required arguments vs arguments in config
$constructor = $reflector->getConstructor();
$numRequired = $constructor->getNumberOfRequiredParameters();
$numSpecified = is_array($args) ? count($args) : 1;
if($numRequired < $numSpecified) {
throw new InvalidFilterDefinitionException(
$serviceId,
$numRequired,
$numSpecified
);
}
EDIT: $constructor can be null...
The short answer is that you simply cannot determine if a set of arguments will allow error-free instantiation of a constructor. As commenters have mentioned above, there's no way to know for sure if a class can be instantiated with a given argument list because there are runtime considerations that cannot be known without actually attempting
instantiation.
However, there is value in trying to instantiate a class from a list of constructor arguments. The most obvious use-case for this sort of operation is a configurable Dependency Injection Container (DIC). Unfortunately, this is a much more complicated operation than the OP suggests.
We need to determine for each argument in a supplied definition array whether or not it matches specified type-hints from the constructor method signature (if the method signature actually has type-hints). Also, we need to resolve how to treat default argument values. Additionally, for our code to be of any real use we need to allow the specification of "definitions" ahead of time for instantiating a class. A sophisticated treatment of the problem will also involve a pool of reflection objects (caching) to minimize the performance impact of repeatedly reflecting things.
Another hurdle is the fact that there's no way to access the type-hint of a reflected method parameter without calling its ReflectionParameter::getClass method and subsequently instantiating a reflection class from the returned class name (if null is returned the param has no type-hint). This is where caching generated reflections becomes particularly important for any real-world use-case.
The code below is a severely stripped-down version of my own string-based recursive dependency injection container. It's a mixture of pseudo-code and real-code (if you were hoping for free code to copy/paste you're out of luck). You'll see that the code below matches the associative array keys of "definition" arrays to the parameter names in the constructor signature.
The real code can be found over at the relevant github project page.
class Provider {
private $definitions;
public function define($class, array $definition) {
$class = strtolower($class);
$this->definitions[$class] = $definition;
}
public function make($class, array $definition = null) {
$class = strtolower($class);
if (is_null($definition) && isset($this->definitions[$class])) {
$definition = $this->definitions[$class];
}
$reflClass = new ReflectionClass($class);
$instanceArgs = $this->buildNewInstanceArgs($reflClass);
return $reflClass->newInstanceArgs($instanceArgs);
}
private function buildNewInstanceArgs(
ReflectionClass $reflClass,
array $definition
) {
$instanceArgs = array();
$reflCtor = $reflClass->getConstructor();
// IF no constructor exists we're done and should just
// return a new instance of $class:
// return $this->make($reflClass->name);
// otherwise ...
$reflCtorParams = $reflCtor->getParameters();
foreach ($reflCtorParams as $ctorParam) {
if (isset($definition[$ctorParam->name])) {
$instanceArgs[] = $this->make($definition[$ctorParam->name]);
continue;
}
$typeHint = $this->getParameterTypeHint($ctorParam);
if ($typeHint && $this->isInstantiable($typeHint)) {
// The typehint is instantiable, go ahead and make a new
// instance of it
$instanceArgs[] = $this->make($typeHint);
} elseif ($typeHint) {
// The typehint is abstract or an interface. We can't
// proceed because we already know we don't have a
// definition telling us which class to instantiate
throw Exception;
} elseif ($ctorParam->isDefaultValueAvailable()) {
// No typehint, try to use the default parameter value
$instanceArgs[] = $ctorParam->getDefaultValue();
} else {
// If all else fails, try passing in a NULL or something
$instanceArgs[] = NULL;
}
}
return $instanceArgs;
}
private function getParameterTypeHint(ReflectionParameter $param) {
// ... see the note about retrieving parameter typehints
// in the exposition ...
}
private function isInstantiable($class) {
// determine if the class typehint is abstract/interface
// RTM on reflection for how to do this
}
}
What would be a good way (along with any pros and cons) of initializing an instance of a PHP class with another object of the same class (ideally in PHP 4.x)?
Here in initialize() is essentially what I'd like to be able to do (example is extremely simplified from my use-case, see below):
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product->initialize($product2);
echo $product->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name) {
$this->name = $name;
}
function initialize($product) {
// I know this cannot be done this way in PHP.
// What are the alternatives and their pros & cons?
$this = $product;
}
}
I know this may not be "good programming practice"; with 20+ years programming experience on other languages I know a bit about what's good and what's not. So hopefully we won't get hung up on if doing this makes sense or not. I have a use-case working with some open-source code that I can't change so please just bear with me on my need for it. I'm actually trying to create an OOP wrapper around some really ugly array code buried deep in the core of WordPress.
I'm trying to write it so in future versions they can move away from the ugly array-based code because everyone will be using the new API that otherwise fully encapsulated these nasty arrays. But to make it work elegantly I need to be able to do the above (in PHP 4.x) and I don't want to write code that just copies the properties.
Thanks in advance for your help.
UPDATE
Many of you are suggesting clone but unless I misunderstand that doesn't address the question. clone makes a copy; that's not the crux of the question. I'm instead trying to get the constructed object to "become" the object passed in. At this point I'm assuming there isn't a way to do that based on the fact that 0 out of 5 answers have suggested anything but I'll wait a bit longer before selecting a best in case it was simply that my questions was unclear.
In PHP 5, object cloning might be more relevant:
http://php.net/manual/en/language.oop5.cloning.php
You can define a special __clone method.
In PHP 4 and 5, you can copy properties via:
function copy($obj)
{
foreach (get_object_vars($obj) as $key => $val)
{
$this->$key = $val;
}
}
However, you wrote "I don't want to write code that just copies the properties," and I'm not exactly sure what you mean by that.
Preferred way of doing this is to use clone keyword and to implement appropriate __clone() method if needed as mentioned by other posters. Another trick way of doing this (con: slow, pros: can be stored, sent over network and works identical in php4/5) is to serialize an object and then unserialize to create new copies of it with identical variable values.
Example:
$productCopy = unserialize(serialize($product));
EDIT: Sorry, misunderstood what you were asking for. You will have to initialize variables of the object being constructed with passed in object's variables inside of the constructor. You can't return a reference to another object from the constructor.
Example:
public function __construct($name, $object = null) {
if($object) {
foreach(get_object_vars($object) as $k => $v) {
$this->$k = $v;
}
} else {
$this->name = $name;
}
}
class Product {
var $name;
function __construct($value) {
if (is_a($value, 'Product')) {
$this->name = $value->name;
} else {
$this->name = $value;
}
}
}
Similarly, you can use instanceof instead of is_a if you prefer (depending on your PHP version).
Now you can pass a Product instance OR a name to the construct.
$product = new Product('Something');
$clone = new Product($product);
This is the best way of doing it so far:
http://www.blrf.net/howto/51_PHP__How_to_control_object_instances_in_PHP_.html
Don't use "new", instead use a static function that returns the instance you want.
I have done the following:
class MyClass {
private static $_instances;
public static function get($id) {
if (!self::$_instances) self::$_instances = Array();
$class = get_called_class();
if (!array_key_exists($class, self::$_instances)) self::$_instances[$class] = Array();
if (!is_numeric($id) || $id == '') throw new Exception('Cannot instantiate a non-numeric ID.');
if (array_key_exists($id, self::$_instances[$class])) return self::$_instances[$class][$id];
else {
self::$_instances[$class][$id] = new static($id);
return self::$_instances[$class][$id];
}
}
function __construct($id=false) {
// new instance code...
// I use $id=false to create new a db table row not load an old one
}
}
Usage:
// New instance
$a = new MyClass();
$a = new MyClass;
// MyClass with $id = 1
$b = MyClass::get(1);
$c = MyClass::get(1);
$d = new MyClass(1);
$b and $c point to the same object, while $d is a new one.
Caveats:
Garbage collection will no longer apply as your instances are stored in a static array
You'll have to change your code to use MyClass::get
Notes in my code:
New instances are called with "new static" instead of "new self" to use late static bindings.
You can set your constructor to private. This will break all your old code if you use "new", but will ensure you don't get double instances or more. You'll have to change a bit in the get function's arguments and code to allow $id=false or $id=-1 or whatever.
maybe
$product = new Product('Widget');
$product2 = new Product(null, $product);
echo $product2->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name, $product = null) {
$this->name = !empty($name) ? $name : $product->name;
}
}
Adding another answer due to it being radically different.
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product =& $product2;
echo $product->name; // echos "Widget #2"
That should work.
I'm using PHP 5.3's class_alias to help process my Symfony 1.4 (Doctrine) forms. I use a single action to process multiple form pages but using a switch statement to choose a Form Class to use.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
class_alias('MyFormPage1Form', 'FormAlias');
break;
...
}
$this->form = new FormAlias($obj);
}
This works brilliantly when browsing the website, but fails my functional tests, because when a page is loaded more than once, like so:
$browser->info('1 - Edit Form Page 1')->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end()->
get('/myforms/edit')->
with('response')->begin()->
isStatusCode(200)->
end();
I get a 500 response to the second request, with the following error:
last request threw an uncaught exception RuntimeException: PHP sent a warning error at /.../apps/frontend/modules/.../actions/actions.class.php line 225 (Cannot redeclare class FormAlias)
This makes it very hard to test form submissions (which typically post back to themselves).
Presumably this is because Symfony's tester hasn't cleared the throughput in the same way.
Is there a way to 'unalias' or otherwise allow this sort of redeclaration?
As an alternate solution you can assign the name of the class to instantiate to a variable and new that:
public function executeEdit(sfWebRequest $request) {
$formType;
switch($request->getParameter('page')) {
case 'page-1':
$formType = 'MyFormPage1Form';
break;
...
}
$this->form = new $formType();
}
This doesn't use class_alias but keeps the instantiation in a single spot.
I do not know for sure if it is possible, but judging from the Manual, I'd say no. Once the class is aliased, there is no way to reset it or redeclare it with a different name. But then again, why do use the alias at all?
From your code I assume you are doing the aliasing in each additional case block. But if so, you can just as well simply instantiate the form in those blocks, e.g.
public function executeEdit(sfWebRequest $request) {
switch($request->getParameter('page')) {
case 'page-1':
$form = new MyFormPage1Form($obj);
break;
...
}
$this->form = $form;
}
You are hardcoding the class names into the switch/case block anyway when using class_alias. There is no advantage in using it. If you wanted to do it dynamically, you could create an array mapping from 'page' to 'className' and then simply lookup the appropriate class.
public function executeEdit(sfWebRequest $request) {
$mapping = array(
'page-1' => 'MyFormPage1Form',
// more mappings
);
$form = NULL;
$id = $request->getParameter('page');
if(array_key_exists($id, $mapping)) {
$className = $mapping[$id];
$form = new $className($obj);
}
$this->form = $form;
}
This way, you could also put the entire mapping in a config file. Or you could create FormFactory.
public function executeEdit(sfWebRequest $request) {
$this->form = FormFactory::create($request->getParameter('page'), $obj);
}
If you are using the Symfony Components DI Container, you could also get rid of the hard coded factory dependency and just use the service container to get the form. That would be the cleanest approach IMO. Basically, using class_alias just feels inappropriate here to me.
function class_alias_once($class, $alias) {
if (!class_exists($alias)) {
class_alias($class, $alias);
}
}
This doesn't solve the problem itself, but by using this function it is ensured that you don't get the error. Maybe this will suffice for your purpose.