Return different objects based on conditional argument - php

I need help with some code desiging. I have a package which is used in many projects.
$p = new Package();
$result = $p->method();
While the Package looks like:
class Package {
public function method($fooArg = 1, $barArg = 1)
{
// some logic
return new SomeClass(
$fizzArg,
$buzzArg
);
}
}
Now, in one project which is using Package I need to change returned class. So, the call will look like this:
$p = new Package();
$result = $p->method(10, 10, true);
And the Package will look like this:
class Package {
public function method($fooArg = 1, $barArg = 1, $condArg = false)
{
// some logic
if (false === $condArg) {
return new SomeClass(
$fizzArg,
$buzzArg
);
}
return new MyNewClass(
$fizzArg,
$fooBarArg
);
}
}
Which design pattern should be used here? I do not want to return different object types based on condition, because it seems very ugly to me.

Since you explicitly set $condArg from the calling script and it is the only argument that control which instance to return, consider to create a second public method to return MyNewClass, and extract some logic into private method to share it between 2 public methods:
class Package {
public function method($fooArg = 1, $barArg = 1)
{
list $fizzArg, $fooBarArg = $this->someLogic($fooArg, $barArg);
return new SomeClass(
$fizzArg,
$buzzArg
);
}
public function newMethod($fooArg = 1, $barArg = 1)
{
list $fizzArg, $fooBarArg = $this->someLogic($fooArg, $barArg);
return new MyNewClass(
$fizzArg,
$fooBarArg
);
}
protected function someLogic($fooArg, $barArg)
{
// some logic
return [
$fizzArg,
$fooBarArg
]
}
}

This could be resolved with a combination of the Factory and Dependency Injection patterns:
The example below is a bit long winded, but it illustrates the basic principle. By providing your Package class with a factory implemented by the specific project. You don't need to change the code of that class. Instead, the changes are made with abstractions via interfaces.
/*
* Shared library
*/
interface IEntity
{
}
interface IFactory
{
/**
* #param $arg1
* #param $arg2
* #return IEntity
*/
public function create($arg1, $arg2);
}
class Package
{
protected $factory;
public function __construct(IFactory $factory)
{
$this->factory = $factory;
}
public function method($arg1, $arg2)
{
return new $this->factory->create($arg1, $arg2);
}
}
/*
* Project A
*/
class FactoryA implements IFactory
{
public function create($arg1, $arg2)
{
return new EntityA();
}
}
class EntityA implements IEntity
{
public function construct($arg1, $arg2)
{
}
}
/*
* Project B
*/
class FactoryB implements IFactory
{
public function create($arg1, $arg2)
{
return new EntityB();
}
}
class EntityB implements IEntity
{
public function construct($arg1, $arg2)
{
}
}

From a purely theoretical point of view, the very idea to use such a package that has to return something else based on where it is used is very ugly. This way it means that the widely-used package depends on a details of the package that uses it and this to me is a signal that you have to scrap the idea entirely and not use this package in this one project.

Your Package seems like a Factory method, it builds objects according to parameters. If I get it right, now you want to have different Packages for different projects with different objects.
Well Abstract factory is what you are looking for.
Make your Package abstract. Have two implementations, and load the proper package in the initiation phase you can use reflection or configuration or whatever PHP lets you do.
Note: It seems that there is some other logic in your Package class, if that logic is independent of building objects extract it to a different class, or vise versa.

since you don't have access to all project using Package, and you can change the source of Package if it does not affect other projects, then it is safer to create a descendant of Package instead. this way, you can change existing methods without affecting other projects, and you can also add new methods.
include('package.php');
class myPackage extends Package(){
public function newMethod($arg1, $arg2){
return new otherClass($arg1, $arg2);
}
//if you want to change existing method
public function method($fooArg = 1, $barArg = 1){
//some new logic
}
}
then you can use it in your project:
$obj = new myPackage();
if ($i_want_regular_class){
$newObj = $obj->method(1, 2); // call parent's method or modified parent's method
} else {
$newObj = $obj->newMethod(3, 4); // call new method
}
as for why i suggest you to create new method is because you already know which object you want to create, hence the argument passed to the method. so instead of putting the conditional in the method, why not put the conditional in the logic and call the appropriate method?

Related

How can I decouple instantiation from implementation for unit testing if DI is not feasible?

I have the following code (simplified and details changed for this question):
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// already doing constructor injection for the data object
public __construct($data_object) {
// details here
}
public function add_new_thing_A($has_relationship) {
$thing_A = new Thing_A();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = new Thing_B();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
In the above example, I have to decouple the instantiation of Thing_A and Thing_B from the add_new_thing method. However, a simple constructor injection will not do for these two classes. This is because I need fresh instances of Thing_A and Thing_B every time add_new_thing is called so that Thing_A can be added to the array_of_thing_A.
How can I make this function unit testable? And more specifically for me to use mocks of Thing_A and Thing_B in testing this function in PHPUnit?
Any suggestions with code example will be appreciated.
Additionally, I would like to mention that Thing_A and Thing_B are used elsewhere in the codebase that I am working with and the code using these classes will eventually need to be unit tested. Solutions that are too localized and would cause repeated code elsewhere will not be too ideal in my situation. Thank you.
As commenter xmike mentioned, you could use the factory pattern. You would inject a factory object through the ctor as well. Then you could have a factory that provides simplified instances of your Thing_A and Thing_B.
class ThingFactory {
public function buildThingA() {
return new Thing_A(); // or MockThing_A if you go the ducktyping route
}
public function buildThingB() {
return new Thing_B();
}
}
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// you could go the typed route and have an interface for this
private $factory;
// already doing constructor injection for the data object
public __construct($data_object, $factory) {
// details here
$this->factory = $factory;
}
public function add_new_thing_A($has_relationship) {
$thing_A = $this->factory->buildThingA();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = $this->factory->buildThingB();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
PHP is such a strange language, you can't assign a class to a variable. But you can do it as a string. Inject ThingA and ThingB on the constructor as strings. You can call new on the string member.
class ThingA {};
class ThingB{};
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
private $_thingA;
private $_thingB;
public function __construct($data_object, $thingA, $thingB) {
$this->_thingA = $thingA;
$this->_thingB = $thingB;
}
public function add_new_thing_A($has_relationship) {
$thing_A = new $this->_thingA();
if ($has_relationship) {
$thing_B = new $this->_thingB();
}
$this->array_of_thing_A[] = $thing_A;
}
}
$model = new model_to_be_tested('foo', 'ThingA', 'ThingB');
$model->add_new_thing_A(true);
There's a live version here: https://repl.it/#rmoskal/InconsequentialAnotherGermanshorthairedpointer
Or provide a static constructor for the class.

Multiple object instantiation prevention?

I have a class with a private variable used to store an object.
I have a function that checks first if that variable already contains an object or not; if not, it instantiates the needed object and sets it to that variable, otherwise it just returns the content of that variable.
I was wondering if the getSessionCustomer() here is an overkill/unnecessary or if it has real benefits. I simply based this on the Album tutorial by Zend, but I haven't been able to fully test it out yet to really see the advantages (or disadvantages). As far as I know it wasn't explained in the docs why this additional function was included.
class JobController extends AbstractActionController
{
private $SessionCustomer;
public function saveJobAction()
{
$SessionCustomer = $this->getSessionCustomer();
if(empty($SessionCustomer->offsetGet('customer_id'))) {
return $this->redirect()->toRoute('login');
} else {
$JobService = $this->getServiceLocator()->get('Job\Factory\JobServiceFactory');
$job_id = $JobService->saveJob();
return $this->redirect()->toUrl('/job/' . $job_id);
}
}
public function viewJobAction()
{
$sm = $this->getServiceLocator();
$SessionCustomer = $this->getSessionCustomer();
if(empty($SessionCustomer->offsetGet('customer_id'))) {
return $this->redirect()->toRoute('login');
} else {
$JobTable = $sm->get('Job\Model\JobTable');
$JobItemTable = $sm->get('Job\Model\JobItemTable');
$jobId = $this->params()->fromRoute('id');
$Job = $JobTable->getJobById($jobId);
$JobItems = $JobItemTable->getJobItemsByJobId($jobId);
$this->layout()->setVariable('title', 'Order #' . $jobId);
$viewModel = new ViewModel();
$viewModel->setVariables(array(
'Job' => $Job,
'JobItems' => $JobItems
));
return $viewModel;
}
}
private function getSessionCustomer()
{
if(!$this->SessionCustomer) {
$this->SessionCustomer = $this->getServiceLocator()->get('Session\Customer');
}
return $this->SessionCustomer;
}
}
I don't think its an overkill, but I usually avoid calling getServiceLocator() in Controllers.
What you are asking about is basically making sure that the controller's dependency requirement is met. You can use a Factory for the same purpose and do this more sophisticated way. You can create a factory and inject the dependencies directly into the controller. This you will never make a call to the non-object variables.
For that you will be required to create a class that implements a FactoryInterface which will have a method createService which will provide you with ServiceLocator. You can use that serviceLocator to get all the dependencies and inject them directly into your Class.

PHP/OOP - Using parent class to run child functions

I'm looking for a way to have a single base class that can be extended by several child classes, only one of which would be active at a time. A very basic example:
class API_Base {
public $context;
public function __construct() {
$this->init()
}
}
class Mailchimp_API extends API_Base {
public function init() {
$this->context = 'mailchimp';
$this->enabled = false;
}
public function add_contact($email_address) {
// mailchimp API for adding contact
}
}
class Infusionsoft_API extends API_Base {
public function init() {
$this->context = 'infusionsoft';
$this->enabled = true;
}
public function add_contact($email_address) {
// infusionsoft API for adding contact
}
}
Each child initializes itself and registers as an option for the user to select. After the user has chosen which integration to use, this is saved to the database. I'd like future access to the API_Base to look something like:
$api = new API_Base();
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);
So when $api->add_contact() is run, it only runs the add_contact() function for the active API integration.
Eventually I'd like to somehow use get_class_methods(); to return the capabilities of just the active API, so functions accessing the API can know what is possible (i.e. some API's support email lists while others don't, or support creating custom fields, etc.).
I've had some success with calling parent::set_context($context); from the enabled class, but I still can't figure out how to get the parent to only execute the methods in the "enabled" child class.
This is not how inheritance works. Child subclasses inherit from their parent class.
To solve your problem you can add a factory method to API_Base which will create API implementation by its type:
class API_Base {
public static function createByType($type)
{
switch ($type) {
case 'mailchimp': return new Mailchimp_API();
case 'infusionsoft': return new Infusionsoft_API();
default: throw new \InvalidArgumentException(spintf('Invalid API type "%s"', $type));
}
}
// other methods
}
and use it like this:
$api = API_Base::createByType($user->selectedApi);
$api->context; // should be "infusionsoft"
$api->add_contact($email_address);
You can consider Abstract Class Implementation . The abstract class works as the , who ever is extending the abstract class can execute the methods it have .
abstract class Something{
function __construct(){
// some stuff
}
function my_func(){
$this->myTest ;
}
abstract function my_func();
}
class Some extends Something{
function __construct(){
parent::__construct() ;
}
function my_test(){
echo "Voila" ;
}
}
I got it working in a way works perfectly for me, thanks to Ihor's advice. Here's what I ended up doing:
In the main plugin file, there's a filterable function where other devs can add new integrations if they need. The first parameter is the slug (for my autoloader) and the second is the class name.
public function get_apis() {
return apply_filters( 'custom_apis', array(
'infusionsoft-isdk' => 'MYPLUGIN_Infusionsoft_iSDK',
'infusionsoft-oauth' => 'MYPLUGIN_Infusionsoft_oAuth',
'activecampaign' => 'MYPLUGIN_ActiveCampaign'
) );
}
Each integration contains the slug and the class name. Then in my API_Base class I have this in the constructor:
class API_Base {
public $available_apis = array();
public $api;
public function __construct() {
$configured_apis = main_plugin()->get_apis();
foreach( $configured_apis as $slug => $classname ) {
if(class_exists($classname)) {
$api = new $classname();
$api->init();
if($api->active == true)
$this->api = $api;
$this->available_apis[$slug] = array( 'name' => $api->name );
if(isset($api->menu_name)) {
$this->available_apis[$slug]['menu_name'] = $api->menu_name;
} else {
$this->available_apis[$slug]['menu_name'] = $api->name;
}
}
}
}
}
And in my main file, after all the includes, I run:
self::$instance->api_base = new API_Base();
self::$instance->api = self::$instance->api_base->api;
Now I can call self::$instance->api->add_contact($email); and it will trigger whichever is the current active API.
It seems to be the best approach as this way I can spin up the API only once when the plugin loads, instead of having to create a new instance each time I want to use it.

Laravel Interface

Recently I came across interface from "Laravel 4 From Apprentice to Artisan" book with the example like this:
interface UserRepositoryInterface {
public function all();
}
class DbUserRepository implements UserRepositoryInterface {
public function all()
{
return User::all()->toArray();
}
}
What is interface? Where to put the interface file?
A Interface is a "contract" between itself and any class that implements the interface.
The contract states that any class that implements the interface should have all methods defined in the interface.
In this case DbUserRepository has to have a method named "all()" or a fatal error will occur when the class is instantiated.
The Interface file can be placed anywhere but the easiest is to put it in the same directory as the class that implements it.
The purpose of the interface is as follows:
Say you want to change your app from using a database (and Eloquent) and now instead you are going store data in JSON files and write your own methods for interacting with your JSON files. Now you can create a new repository e.g. JSONRepository and have it implement UserRepositoryInterface and because the interface forces you to define all the same methods that is defined in the interface, you can now be sure that your app will continue to work as it did. All this without you having to modify existing code.
The database example doesn't really make much real world sense to me because it is unlikely that I would change my storage system so drastically and the example always makes it seem like interfaces only have this one very small use case, which cant be further from the truth.
Coding to a interface has many benefits for you and your application.
Another example of interfaces in use can be:
Let's say you have a Calculator class and initially it has two operations it can perform (addition and multiplication). But a few weeks later you need to add another operation (e.g. subtraction), now normally this would mean you have to modify the calculator class and thus risk breaking it.
But if you are using a interface you can just create the Subtraction class and have it implement the CalculationInterface and now your app has a new operation without you touching existing code.
Example:
Calculator.php
<?php
class Calculator {
protected $result = null;
protected $numbers = [];
protected $calculation;
public function getResult()
{
return $this->result;
}
public function setNumbers()
{
$this->numbers = func_get_args();
}
public function setCalculation(CalculationInterface $calculation)
{
$this->calculation = $calculation;
}
public function calculate()
{
foreach ($this->numbers as $num)
{
$this->result = $this->calculation->run($num, $this->result);
}
return $this->result;
}
}
CalculationInterface.php
<?php
interface CalculationInterface {
public function run($num, $current);
}
Addition.php
<?php
class Addition implements CalculationInterface {
public function run($num, $current)
{
return $current + $num;
}
}
Multiplication.php
<?php
class Multiplication implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to multiply $num with null */
if (is_null($current))
return $num;
return $current * $num;
}
}
Then to run the calculate method:
$this->calc = new Calculator;
$this->calc->setNumbers(5, 3, 7, 10);
$this->calc->setCalculation(new Addition);
$result = $this->calc->calculate(); //$result = 25
Now if you want to add a new operation let's say Subtraction you just create the Subtraction class and have it implement the CalculationInterface:
<?php
class Subtraction implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to subtract from null */
if (is_null($current))
return $num;
return $current - $num;
}
}
Then to run it:
$this->calc = new Calculator;
$this->calc->setNumbers(30, 3, 7, 10);
$this->calc->setCalculation(new Subtraction);
$result = $this->calc->calculate(); //$result = 10
So in this example you are breaking your functionality up into smaller classes so that you can add, remove or even change them without breaking something else.

Modular design pattern

I'm trying to decide the design of a system which is meant to allow for a high amount of extensiblity. From what I can tell, a pattern such as the abstract factory would not allow for overriding of the base methods, apart from duplicating code (as demonstrated below).
I've done some preliminary research into aspect oriented programming and it seems to be along the lines of what I'm looking for but I'm having a difficult time wrapping my head around the specifics.
abstract class Object {
protected $object_id;
protected $name;
function LoadObjectData()
{
$file_contents = readfile('object'.$object_id.'.data');
$data = array();
// parse file contents into $data array...
return $data;
}
function Create()
{
$data = $this->LoadObjectData();
$name = $data['name'];
return $data;
}
}
class User extends Object {
protected $email_address;
function Create()
{
$data = parent::Create();
$this->email_address = $data['email_address'];
return $data;
}
}
//----------Module 1-MySQL Lookup-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
$data = array();
$result = mysql_query("SELECT...");
// construct array from result set
return $data;
}
//----------Module 2-Cache Machine-------------
/*
* Redefine Object::LoadObjectData() as follows:
*/
function LoadObjectData()
{
if (exists_in_cache($object_id)) {
return get_cached_object($object_id);
}
$data = parent::LoadObjectData();
cache_object($object_id, $data);
return $data;
}
(This is sort of a poor example, but hopefully it helps to get my point across)
The intended system would have a very large proportion of methods available to be extended and I would like to minimize the extra effort and learning necessary for developers.
Is AOP exactly what I'm looking for, or is there a better way to deal with this?
Thanks!
So, you want to use a decorator pattern without defining the decorator itself.
If yes, then it's a monkeypatching and can be done with aspect-oriented tools. This can be solved easily with following extensions and frameworks:
PHP Runkit Extension
Go! Aspect-Oriented framework for PHP
PHP-AOP Extension.
You don't have to declare the base class as an abstract class. You can make it a regular class and have it load and instantiate other classes based on passed construct parameters. The constructor can return an instance of a class, not just the class the constructor is in. To avoid duplicating code, you can mix static with instantiated functions and variables. Just remember that a static function or variable is the same for ALL instances. Change a static variable in one and it is changed for all instances. A rather basic example of a plugin architecture.
class BaseObject {
protected static $cache = array();
public function __construct($load_plugin) {
require_once($load_plugin.'.class.php');
$object = new $load_plugin();
return $object;
}
public static function cacheData($cache_key, $data) {
self::$cache[$cache_key] = $data;
}
}
class Plugin extends BaseObject {
public function __construct() {
}
public function loadData() {
// Check the cache first
if ( !isset(self::$cache[$cache_key]) ) {
// Load the data into cache
$data = 'data to cache';
self::cacheData($cache_key, $data);
}
return self::$cache[$cache_key];
}
}

Categories