multi-level inheritance substitution - php

I want to write a module (framework specific), that would wrap and extend Facebook PHP-sdk (https://github.com/facebook/php-sdk/). My problem is - how to organize classes, in a nice way.
So getting into details - Facebook PHP-sdk consists of two classes:
BaseFacebook - abstract class with all the stuff sdk does
Facebook - extends BaseFacebook, and implements parent abstract persistance-related methods with default session usage
Now I have some functionality to add:
Facebook class substitution, integrated with framework session class
shorthand methods, that run api calls, I use mostly (through BaseFacebook::api()),
authorization methods, so i don't have to rewrite this logic every time,
configuration, sucked up from framework classes, insted of passed as params
caching, integrated with framework cache module
I know something has gone very wrong, because I have too much inheritance that doesn't look very normal. Wrapping everything in one "complex extension" class also seems too much. I think I should have few working togheter classes - but i get into problems like: if cache class doesn't really extend and override BaseFacebook::api() method - shorthand and authentication classes won't be able to use the caching.
Maybe some kind of a pattern would be right in here? How would you organize these classes and their dependencies?
EDIT 04.07.2012
Bits of code, related to the topic:
This is how the base class of Facebook PHP-sdk:
abstract class BaseFacebook {
// ... some methods
public function api(/* polymorphic */)
{
// ... method, that makes api calls
}
public function getUser()
{
// ... tries to get user id from session
}
// ... other methods
abstract protected function setPersistentData($key, $value);
abstract protected function getPersistentData($key, $default = false);
// ... few more abstract methods
}
Normaly Facebook class extends it, and impelements those abstract methods. I replaced it with my substitude - Facebook_Session class:
class Facebook_Session extends BaseFacebook {
protected function setPersistentData($key, $value)
{
// ... method body
}
protected function getPersistentData($key, $default = false)
{
// ... method body
}
// ... implementation of other abstract functions from BaseFacebook
}
Ok, then I extend this more with shorthand methods and configuration variables:
class Facebook_Custom extends Facebook_Session {
public function __construct()
{
// ... call parent's constructor with parameters from framework config
}
public function api_batch()
{
// ... a wrapper for parent's api() method
return $this->api('/?batch=' . json_encode($calls), 'POST');
}
public function redirect_to_auth_dialog()
{
// method body
}
// ... more methods like this, for common queries / authorization
}
I'm not sure, if this isn't too much for a single class ( authorization / shorthand methods / configuration). Then there comes another extending layer - cache:
class Facebook_Cache extends Facebook_Custom {
public function api()
{
$cache_file_identifier = $this->getUser();
if(/* cache_file_identifier is not null
and found a valid file with cached query result */)
{
// return the result
}
else
{
try {
// call Facebook_Custom::api, cache and return the result
} catch(FacebookApiException $e) {
// if Access Token is expired force refreshing it
parent::redirect_to_auth_dialog();
}
}
}
// .. some other stuff related to caching
}
Now this pretty much works. New instance of Facebook_Cache gives me all the functionality. Shorthand methods from Facebook_Custom use caching, because Facebook_Cache overwrited api() method. But here is what is bothering me:
I think it's too much inheritance.
It's all very tight coupled - like look how i had to specify 'Facebook_Custom::api' instead of 'parent:api', to avoid api() method loop on Facebook_Cache class extending.
Overall mess and ugliness.
So again, this works but I'm just asking about patterns / ways of doing this in a cleaner and smarter way.

Auxiliary features such as caching are usually implemented as a decorator (which I see you already mentioned in another comment). Decorators work best with interfaces, so I would begin by creating one:
interface FacebookService {
public function api();
public function getUser();
}
Keep it simple, don't add anything you don't need externally (such as setPersistentData). Then wrap the existing BaseFacebook class in your new interface:
class FacebookAdapter implements FacebookService {
private $fb;
function __construct(BaseFacebook $fb) {
$this->fb = $fb;
}
public function api() {
// retain variable arguments
return call_user_func_array(array($fb, 'api'), func_get_args());
}
public function getUser() {
return $fb->getUser();
}
}
Now it's easy to write a caching decorator:
class CachingFacebookService implements FacebookService {
private $fb;
function __construct(FacebookService $fb) {
$this->fb = $fb;
}
public function api() {
// put caching logic here and maybe call $fb->api
}
public function getUser() {
return $fb->getUser();
}
}
And then:
$baseFb = new Facebook_Session();
$fb = new FacebookAdapter($baseFb);
$cachingFb = new CachingFacebookService($fb);
Both $fb and $cachingFb expose the same FacebookService interface -- so you can choose whether you want caching or not, and the rest of the code won't change at all.
As for your Facebook_Custom class, it is just a bunch of helper methods right now; you should factor it into one or more independent classes that wrap FacebookService and provide specific functionality. Some example use cases:
$x = new FacebookAuthWrapper($fb);
$x->redirect_to_auth_dialog();
$x = new FacebookBatchWrapper($fb);
$x->api_batch(...);

It is indeed too much inheritance. Looks like a job for Facade design pattern. Use composition instead of inheritance to have more flexibility. Delegate any methods you use to appropriate objects.
For example if any of the underlying classes changes, you can just change your methods to adapt to the changes, and you won't have to worry about overriding any of the parent methods.
Generally yes, it is not a good idea to assign multiple responsibilities to one class. Here, the responsibility of the class would be to represent an external API.

I have done some thing like that for yahoo sdk let me put it, give it a try :)
Lets assume Facebook is the class in sdk you are using for all end method calls.
You can create a new class(as your frame work allows) and assign a variable of the class to the instance of the Facebook Class .
Use __call() for all methods of Facebook and put your custome ones in the wrapper class.
for all undefined methods it wrapper it will go to Facebook Class and there is no inheritance involved at all.
It worked for me . Hope it helps :)
Class MyWrapper
{
protected $facebook;
public function __construct()
{
$this->facebook = new FaceBook();
}
public function __call($method,$args)
{
return $this->facebook->$method($args);
}
///define Your methods //////////
///////////////////////////////////
}
$t = new MyWrap;
$t->api(); // Whatever !!!!
edited :
You don't need to create multiple wrapper for more than one classes following can be done,you just need to take care at the method call time, have to suffix the variable name holding instance of the wrapped class.
Class MyWrapper
{
protected $facebook;
protected $facebookCache;
public function __construct()
{
$this->facebook = new FaceBook();
$this->facebookCache = new FacebookCache();
}
public function __call($method,$args)
{
$method = explode('_',$method);
$instance_name = $method[0];
$method_name = $method[1];
return $this->$instance_name->$method_name($args);
}
///define Your methods //////////
///////////////////////////////////
}
$t = new MyWrap;
$t->facebook_api(); // Whatever !!!!
$t->facebookCache_cache();

i think repository design pattern will be better in this situation. although i am not from php but as per oops it should solve your issue..

Related

Is there any way to add methods to php classes like in javascript?

I'm writing a plugin for WooCommerce, Today something came across my mind suddenly, I was thinking if there is a way to expend PHP Classes like the way we do that in JavaScript without touching anything in the PHP class( Since WooCommerce will get updated and our changed will be gone ).
I found solutions like using __call method but like i said, the class shouldn't be edited. I want to be able to use something like this WC()->My_custom_method() without touching Woocommerce class.Is it even possible?
For example in JavaScript all we need to do is :
Foo.prototype.bar = function (){
// ...
}
Foo.bar();
PHP does not have prototypical inheritance and classes have no prototype like in JavaScript. You can simulate what you are asking with some hackery, but that will ultimately result in hard to maintain code. So don't go there.
There is a GitHub project at https://github.com/bdelespierre/prototype.php with a Proof of Concept implementation. You might find it interesting to study.
Needless to say, if your aim is just to add some functionality to a class, you can still use PHP's inheritance, e.g. extend the WooCommerce class and add your modifications in the subclass. Then use an instance of that new class instead, e.g.
class MyClass extends SomeWooCommerceClass {
public function bar() {
// your own implementation of bar
}
}
$myObj = new MyClass();
$myObj->bar();
If your aim is to change the behavior of an existing object instance, consider wrapping the instance into a Decorator, e.g.
class WooCommerceDecorator {
private $instance;
public function __construct(SomeWooCommerceClass $instance) {
$this->instance = $instance;
}
public function foo() {
$original = $this->instance->foo();
$original+= 42;
return $original;
}
// … more methods
Then use it by passing the object to the decorator:
$woo = new SomeWooCommerceClass();
$decoratedWoo = new WooCommerceDecorator($woo);
echo $decoratedWoo->foo();
Also see the PHP manual on Inheritance
Your best call would be to use extended class.
class MyClass extends Foo {
public function methodInFoo() {
echo "I Am extended";
parent::methodInFoo();
}
public function additionalFunction() {
echo "I am new method that is not presented in Foo";
}
}
That way you even can have Composer to auto-update core classes and while you are using this extended class, you will have your own functions along with all functionality in core class.
I don't recommend this but you could do something like
class Test {
private $myMethods = array();
public addMethod($name, $func) {
$myMethods[$name] = $func;
}
public callMethod() {
$myMethods[$name]();
}
}
....
$test->addMethod("doSomething", function(){ echo 123;});
$test->callMethod("doSomething");
I didn't test the code it's just an idea

Where to implement a factory method?

I've been trying to grasp OOP concepts and while I do get the general ideas behind most of them, I often find myself in need of some advice regarding their practical implementation. One of such cases is the factory method.
I'm writing a PHP app that's going to handle requests incoming from both web and command-line interface, so I came up with the following simple class inheritance structure to cover both types of requests:
abstract class Request {
}
class HttpRequest extends Request {
}
class CliRequest extends Request {
}
Now I need a factory method that would return a concrete Request instance, depending on the value returned by php_sapi_name():
public function create() {
if(php_sapi_name() === 'cli')
return new CliRequest();
else
return new HttpRequest();
}
My question is: where do I put it? I can think of at least three possibilities:
1) Static method in a separate class:
class RequestFactory {
public static function create() {
// ...
}
}
2) Regular method in a separate class (would require instantiating the class first):
class RequestFactory {
public function create() {
// ...
}
}
3) Static method in the abstract parent class:
abstract class Request {
public static function create() {
// ...
}
}
What are the pros and cons of each solution and which would be considered "proper" and why?
All these possibilities will work as expected. I don't really get any "cons" as it fulfils, what is IMHO, your encapsulation objective.
Now, let's look at Factory Method pattern essence:
Define an interface for creating an object, but let the classes that
implement the interface decide which class to instantiate. The Factory
method lets a class defer instantiation to subclasses.
I'm not sure if what you're willing to do perfectly matches this definition.
Instead, it looks like you want to implement something called "Simple Factory" in which instantiation process is encapsulated into a class.
But having this kind of method directly into the abstract class that defines the interface of your "Request" objects doesn't look like a bad idea.
As Nicolas said, it's a rather common pattern in Java, C#, and Cocoa lands.
For these reasons, my choice would go to 3rd option
So, I have an idea, to do what I think you want, using method overloading.
class Request {
private $request;
private $valid = true;
private $type;
private $vars;
private $methods;
public function __construct() {
$this->type = php_sapi_name() === 'cli' ? 'cli' : 'http';
if($this->is_cli()) $this->request = new CliRequest();
else if($this->is_http()) $this->request = new HttpRequest();
else {
$this->valid = false;
return;
}
$this->vars = get_class_vars($this->request);
$this->methods = get_class_methods($this->request);
}
public function __get( $var ){
if(!$this->valid) return false;
if(!in_array($var, $this->vars)) return false;
return $this->request->$var;
}
public function __set( $var , $val ){
if(!$this->valid) return false;
if(!in_array($var, $this->vars)) return false;
return $this->request->$var = $val;
}
public function __call( $meth, $args ){
if(!$this->valid) return false;
if(!in_array($meth, $this->methods)) return false;
return call_user_func_array($this->request->$var, $args);
}
public function is_cli( ){
return $this->type == 'cli';
}
public function is_http( ){
return $this->type == 'http';
}
}
// Then, when calling the function...
$request = new Request;
$request->variable; // will get the variable from the Cli or Http request class
$request->method("a","b","c"); // Will run the method from the Cli or Http request class
To create truely loosely coupled code you could use Ray.Di or Injektor and do something similar to the following:
<?php
use Ray\Di\Di\Inject;
use Ray\Di\Di\Scope;
/**
* #Scope("Singleton")
*/
abstract class Request {
}
class HttpRequest extends Request {
}
class CliRequest extends Request {
}
class ARequestConsumer {
/* #var Request */
private $request;
public function __construct( Request $request )
{
$this->request = $request;
}
public function foo()
{
//...
}
}
class Global extends Ray\Di\AbstractModule {
public function configure()
{
$this->bind( 'Request' )
->toProvider( 'RequestProvider' );
}
}
class RequestProvider implements \Ray\Di\ProviderInterface {
/**
* #return Request
*/
public function get()
{
//.. factory method logic goes here that produces a concrete instance of Request
}
}
$injector = Injector::create([new Global]);
$consumer = $injector->getInstance('ARequestConsumer');
$consumer->foo();
Using a static method in the parent class doesn't seem an awful solution to me at all.
Take a look at the Calendar class in Java: There's a getInstance method (many actually) which return a Calendar instance depending on your Locale, and some others criteria.
In this case I rather use the Base Abstract class as the creator of the Instance using the static method (3rd Option)
I will use an external class, like in the first option when I need to create some dependencies that Can break the encapsulation like having different dependencies for different implementations. and will turn the class less maintainable.
Design patterns are not restricted to OOP and a lot of implementations of OOP design patterns are written with some memory management in thought.
I come from the Java world and in Java you would have to use a strict OOP design pattern. Simply because everything in Java is an object. Sometimes you must create an object and a method even if it is actually not needed for the pattern itself. The factory method design pattern is such an example.
It is very good to design by interface for the implementations of the factory, but you don't need a class and method to implement the factory.
The reason that an implementation of a design pattern is sometimes confusing is that the programming language sometimes requires an implementation that is not strictly needed in the design pattern itself. The creation of a class with a method in the factory method is such an example.
My solution is not purely OOP, but PHP is that not too and in the long run is it not about OOP in my opinion, but about the best implementation of the design pattern factory method.
I think that the elegancy of PHP is that it combines best of both worlds. It delivers a solid OOP design possibility, yet it has not thrown away the good elements of procedural programming.
You can simply create code like this:
function createRequest($pRequesttype){
switch($pRequesttype){
case "cli":
$tmp = new CliRequest();
break;
case "http":
$tmp = new HttpRequest();
break;
default:
$tmp = new DefaultRequest();
}
return $tmp;
}
Always return a default implementation to handle the request.
A switch statement is the best choice to extend the number of choices in a software engineer friendly way.
Now have you the call php_sapi_name in your create function. I advice you to get it out the implementation of the function. It is best practice to let a function do one job only and getting the request and handling the request are two functions. Make a createRequest function that has a parameter like I showed you.
To answer your question:
1, 2 or 3? Uh, 4 actually. :-) If 1, 2 or 3?
Definitely 1, because I don't want to load too much classes for a simple method, but to simplify this situation I have proposed solution 4.
I would not use method 2, because it is not efficient to create a class for one moment in time. I would consider it best practice, but not the best practical implementation. If using this solution, then please also support the class with an interface for factories in general. Best OOP design, but not best practical implementation.
I would certainly not use method 3, because abstract classes are there to abstract data objects and interfaces to abstract behaviour. A factory method is an abstraction of an interface, never of an abstract class.

How do I instantiate my database object, to be used in other classes?

I've encountered an architectural issue with my application. I've rolled my own (very basic) MVC, and one of my models is a database object: class MySQLDatabase { }
There's a number of places in which I'd want to use my database object, without creating duplicate instances. Inside my controller, I have declared public $db; and within the __construct { } I have $this->db = new MySQLDatabase;
Question:
How do I use $db within my other classes--they're all instantiated within the controller's __construct { } as well... would I declare global $db at the top of all my classes that require database connectivity?
I'm used to global variables being declared in the global scope as regular variables, and then using the global keyword to reference the global scope... I'm not sure if that applies to variables declared within a class (my controller.)
I would stay away from using globals or the Singleton pattern (which is essentially a global anyway), and try and find some alternatives. Additionally you are talking about a database connection, by using the Singleton pattern you are saying that there will never be more than one database connection, whilst that is generally true in smaller applications, as they grow larger you won't be able to accomodate multiple connections.
Once you make something global then you lose the automatic contraints of where it can be used/modified. Using MVC a view shouldn't be used for anything other than to display data, by using a global/singleton it is up to the developer to not make use of the globals. Whereas with a different design they don't have that option.
You mentioned you've created your own MVC framework, so I imagine the classes you want to use it in are your models? Correct me if they are anywhere else.
If your models extend from a common base class then you could pass your database object to that class as a static variable which can be assigned to any new instances in the construct or using a factory method in the factory method.
This isn't to say that globals or singletons should be avoided at all costs, but definitely try consider the alternatives that could lead to a neater design.
Here's some reading on the Singleton pattern if you're interested:
Patterns I Hate #1: Singleton
Why Singletons are Evil
Singleton Considered Stupid
Use your singletons wisely
There are many more out there...
If I understand correctly you have a single controller that instantiates the database object and it also takes care of instantiating other classes. If so, you could implement some form of dependency injection either passing the db object in the constructor of the other classes or creating a setter method.
A good blog article on the subject:
http://www.potstuck.com/2009/01/08/php-dependency-injection/
I Think you going about this the wrong way, you should not be performaing quesries to the database from you controller.
this means that the below is invalid.
class ControllerIndex extends Controller
{
public function index()
{
$this->db->selectAll("table");
}
}
There should be a layer that separates your controller from your database interface, this is where a Model comes in.
You should have a models folder that contain classes for actions taken such as users,posts,logging etc.
class Users_Model extends Model
{
public function getUser($id)
{
}
}
The model class should be part of your system core, and should extend your Database Class, this way within your main controller you should be loading the models via the ModelLoader class.
for example:
class ModelLoader
{
private $models = array();
public function __get($model)
{
//load (/application/models/?.php) and initiate it here
//Storing it in models array above
}
}
Then in your main controller:
class Controller
{
private $model;
public function __construct()
{
$this->model = new ModelLoader;
}
}
this way your bringing your loader into scope for the child controller:
class Controller_index extends Controller
{
public function index()
{
$user = $this->model->users->getUser(22);
}
}
Hope this helps!
I think what you need here is a singleton for you Database object :)
See here for more details : http://en.wikipedia.org/wiki/Singleton_pattern
Edit with sample singleton for php :
<?php
class UniqueObject {
private $_uniq = null;
//private cause you don't want to instanciate the classic way
private function __construct() {
//...
}
//check if unique object exists or not, then return it
public static function uniq() {
if(!self::$_uniq)
self::$_uniq = new UniqueObject();
return self::$_uniq;
}
}
//call your unique object whenever you need it
UniqueObject::uniq();
?>
(it's late, i hope i didn't do any mistake :))
Don't use singletons. It's much better to explicitly pass around data. For example:
abstract class Controller {
private static $conn; // could be an array for multiple connections
final protected function getDBConnection() {
if (!$this->conn) {
$this->conn = new DBConnection();
}
return $this->conn;
}
abstract public function process(Request $r);
}
class HomePageController extends Controller {
public function process(Request $r) {
$results = $this->getDBConnection()->query('SELECT stuff FROM foo;');
// do stuff with $results
}
}
You could also have an explicit model object you pass around, e.g. the one that represents the user, but that may be overkill for your project.
You'll need to use a singleton pattern. They give examples in the php docs
<?php
class Example
{
// Hold an instance of the class
private static $instance;
// A private constructor; prevents direct creation of object
private function __construct()
{
echo 'I am constructed';
}
// The singleton method
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Example method
public function bark()
{
echo 'Woof!';
}
// Prevent users to clone the instance
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
?>
http://php.net/manual/en/language.oop5.patterns.php

Advice on Factory Method

Using php 5.2, I'm trying to use a factory to return a service to the controller. My request uri would be of the format www.mydomain.com/service/method/param1/param2/etc. My controller would then call a service factory using the token sent in the uri. From what I've seen, there are two main routes I could go with my factory.
Single method:
class ServiceFactory {
public static function getInstance($token) {
switch($token) {
case 'location':
return new StaticPageTemplateService('location');
break;
case 'product':
return new DynamicPageTemplateService('product');
break;
case 'user'
return new UserService();
break;
default:
return new StaticPageTemplateService($token);
}
}
}
or multiple methods:
class ServiceFactory {
public static function getLocationService() {
return new StaticPageTemplateService('location');
}
public static function getProductService() {
return new DynamicPageTemplateService('product');
}
public static function getUserService() {
return new UserService();
}
public static function getDefaultService($token) {
return new StaticPageTemplateService($token);
}
}
So, given this, I will have a handful of generic services in which I will pass that token (for example, StaticPageTemplateService and DynamicPageTemplateService) that will probably implement another factory method just like this to grab templates, domain objects, etc. And some that will be specific services (for example, UserService) which will be 1:1 to that token and not reused. So, this seems to be an ok approach (please give suggestions if it is not) for a small amount of services. But what about when, over time and my site grows, I end up with 100s of possibilities. This no longer seems like a good approach. Am I just way off to begin with or is there another design pattern that would be a better fit? Thanks.
UPDATE: #JSprang - the token is actually sent in the uri like mydomain.com/location would want a service specific to loction and mydomain.com/news would want a service specific to news. Now, for a lot of these, the service will be generic. For instance, a lot of pages will call a StaticTemplatePageService in which the token is passed in to the service. That service in turn will grab the "location" template or "links" template and just spit it back out. Some will need DynamicTemplatePageService in which the token gets passed in, like "news" and that service will grab a NewsDomainObject, determine how to present it and spit that back out. Others, like "user" will be specific to a UserService in which it will have methods like Login, Logout, etc. So basically, the token will be used to determine which service is needed AND if it is generic service, that token will be passed to that service. Maybe token isn't the correct terminology but I hope you get the purpose.
I wanted to use the factory so I can easily swap out which Service I need in case my needs change. I just worry that after the site grows larger (both pages and functionality) that the factory will become rather bloated. But I'm starting to feel like I just can't get away from storing the mappings in an array (like Stephen's solution). That just doesn't feel OOP to me and I was hoping to find something more elegant.
I think there is no way to avoid this token-service-mapping maintaining work when your site growing large. No matter how you implement this list, switch block, array, etc. this file will become huge someday. So my opinion is to avoid this list and make each token a service class, for those generic services, you can inherit them, like this
class LocationService extends StaticPageTemplateService {
public function __construct(){
parent::__construct('location');
}
}
class ServiceFactory {
public static function getInstance($token) {
$className = $token.'Service';
if(class_exist($className)) return new $className();
else return new StaticPageTemplateService($token);
}
}
in this way, you can avoid to edit the factory class file each time a token added or changed, you just need to change the specific token file.
I have a better factory pattern solution that will allow you to add new services without doing anything more than creating a new class for that specific service. Outlined below:
For the Factory:
class ServiceFactory{
private static $instance = null;
private static $services = array();
private function __construct(){
// Do setup
// Maybe you want to add your default service to the $services array here
}
public function get_instance(){
if($this->instance){
return $this->instance;
}
return $this->__construct();
}
public function register_service($serviceName, $service){
$this->services[$serviceName] = $service;
}
public function get_service($serviceName){
return $this->services[$serviceName]->get_new();
}
}
An Abstract Service:
include('ServiceFactory.php');
class AbstractService{
public function __construct($serviceName){
$factory = ServiceFactory::get_instance();
$factory->register_service($serviceName, $this);
}
public function get_new(){
return new __CLASS__;
}
}
And then a concrete service:
include('AbstractService.php');
class ConcreteService extends AbstractService{
// All your service specific code.
}
This solution makes your dependencies one way and you can add new services by simply extending the AbstractService, no need to modify any existing code. You call into the factory with the get_service('news') or whichever you want, the factory looks up the associated object in its $services array and calls the get_new() function on that particular object which gets you a new instance of the specific service to work with.
I'm not a PHP developer, so I'm not going to attempt to show any code, but here's what I would do. I would implement the Strategy Pattern and create an IServiceProvider interface. That interface could have a GetService() method. Then you would create four new objects: LocationService, ProductService, UserService, and DefaultService, all of which would implement the IServiceProvider interface.
Now, in your factory, the constructor would take in a IServiceProvider and have one public GetService() method. When the method is called, it will use the strategy of the injected IServiceProvider. This improves extensability as you won't have to open the Factory everytime you have a new service, you would just create a new class that implements IServiceProvider.
I decided quick to mock this up quick in C# so you would have an example. I understand this isn't the language you are using, but maybe it will help clarify what I'm saying. Code shown below.
public interface IServiceProvider
{
Service GetService();
}
public class UserServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class StaticPageTemplateServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class DynamicPageTemplateServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class DefaultServiceProvider : IServiceProvider
{
public Service GetService()
{
//perform code to get & return the service
}
}
public class ServiceFactory
{
public ServiceFactory(IServiceProvider serviceProvider)
{
provider = serviceProvider;
}
private IServiceProvider provider;
public Service GetService()
{
return provider.GetService();
}
}
Service factory implementation (with an interface that we'll use in concrete classes):
class ServiceFactory
{
private static $BASE_PATH = './dirname/';
private $m_aServices;
function __construct()
{
$this->m_aServices = array();
$h = opendir(ServiceFactory::$BASE_PATH);
while(false !== ($file = readdir($h)))
{
if($file != '.' && $file != '..')
{
require_once(ServiceFactory::$BASE_PATH.$file);
$class_name = substr($file, 0, strrpos($file, '.'));
$tokens = call_user_func(array($class_name, 'get_tokens'));
foreach($tokens as &$token)
{
$this->m_aServices[$token] = $class_name;
}
}
}
}
public function getInstance($token)
{
if(isset($this->m_aServices[$token]))
{
return new $this->m_aServices[$token]();
}
return null;
}
}
interface IService
{
public static function get_tokens();
}
$BASE_PATH.'UserService.php':
class UserService implements IService
{
function __construct()
{
echo '__construct()';
}
public static function get_tokens()
{
return array('user', 'some_other');
}
}
So, what we're doing essentially is self-registering all tokens for any concrete class implementation. As long as your classes reside in $BASE_PATH, they'll automatically be loaded by the ServiceFactory when it's instantiated (of course, you could change ServiceFactory to provide this via static methods if you wanted to).
No need to have a big switch statement providing access to concrete implementations as they're all help in an internal map that's built by the get_tokens() function that are implemented at the concrete class level. The token->class relationship is stored in a 1:1 map within the service factory, so you'd need to refactor this if you're chaining tokens for whatever reason.
Here's how I do a singleton factory (comments removed for brevity):
Updated to better serve your purpose.
class ServiceFactory {
private static $instance;
private function __construct() {
// private constructor
}
public function __clone() {
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
public static function init() {
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
public function get_service($name, $parameter) {
$name .= 'TemplateService';
return $this->make_service($name, $parameter);
}
private function make_service($name, $parameter) {
if (class_exists($name)) {
return new $name($parameter);
} else {
throw new LogicException('Could not create requested service');
return false;
}
}
}
In it's simplest form like this, just pass a string name of the service:
function whatever() {
$ServiceFactory = ServiceFactory::init();
$new_service = $ServiceFactory->get_service('StaticPage', 'location');
return $new_service;
}
And some that will be specific services (for example, UserService) which will be 1:1 to that token and not reused. So, this
seems to be an ok approach (please give suggestions if it is not) for a small amount of services. But what about when, over
time and my site grows, I end up with 100s of possibilities. This no longer seems like a good approach. Am I just way off
to begin with or is there another design pattern that would be a better fit? Thanks.
Sorry to say, but I think you're now trying to solving a problem that you've created for yourself.
the token is actually sent in the uri like mydomain.com/location would want a service specific to
loction and mydomain.com/news would want a service specific to news. Now, for a lot of these, the
service will be generic. For instance, a lot of pages will call a StaticTemplatePageService in which
the token is passed in to the service. That service in turn will grab the "location" template or
"links" template and just spit it back out.
Some have already suggested using a Dependency Injection Container to solve the whole factory issue, but I wonder why there is a need for a factory in the first place? You seem to be writing a Controller (I guess), that can generate a response for a multitude of different types of request, and you're trying to solve it all in one class. I'd instead make sure that the different requests (/location, /news) map to dedicated, small, readable controllers (LocationController, NewsController). As one controller needs only one service this should be much easier to write, maintain and expand.
That way, you solve the dependencies in dedicated, concise, readable classes instead of one giant God class. That means you'll have no issues with a switch of hundreds of lines either, you should just map "location" to LocationController, "news" to NewsController, etc. A lot of PHP frameworks these days use a FrontController for that, and I imagine that is the way to go for you as well.
PS: to make sure the NewsService actually makes into the NewsController, I would suggest using a dependency injection container. It makes your life easier ;)

Intercepting calls to methods in PHP

I have a class with static methods, and I would like to intercept method calls before the methods are called.
So if I call
$model = DataMapper::getById(12345);
then I want some method in DataMapper to be called before this method is called, and then optionally this intercepting method can subsequently call self::getById(12345). Is there a way to do this?
I am implementing Memcache on my server, so that is why I want to intercept method calls. I don't want the static methods to query the database if models are already cached, and I also don't want to have to modify hundreds of different mapper methods, redundantly, to support memcache.
I am running PHP 5.2.6.
This'd do the job:
Triggering __call() in PHP even when method exists
Just declare your static methods as protected so they're inaccessible outside the class and get the __callStatic() magic method to invoke them.
Edit: oops, you'll be needing 5.3 to do it...
This is one example where you might want to consider ditching static methods in favor of polymorphism. If your data-mapper was an interface then you could have two implementations, one for the database and one for memcache:
interface DataMapper {
public function getById($id);
// other data mapper methods
}
class DataMapper_DB implements DataMapper {
public function getById($id) {
// retrieve from db
}
// other methods
}
class DataMapper_Memcache implements DataMapper {
private $db;
public function __construct(DataMapper_DB $db, $host, ...) {
$this->db = $db;
// other set up
}
public function getById($id) {
// if in memcache return that
// else
$record = $this->db->getById($id);
// add record to memcache
return $record
}
//other methods
}
I just came up with a way to intercept method calls in PHP - Check it out.
It's just a basic example, and classes that want to be interceptible have to "opt in" - you can't interfere with the behavior of classes that don't implement the two magic methods.
I don't know if this meets your needs - but this pattern can be implemented without code generation or runtime bytecode hacks, and that's gotta be a plus ;-)
I guess you could have created some magic with runkit, but you would need to compile the extension from cvs, since the latest version does not support 5.2.x
Example:
<?php
/* Orig code */
class DataMapper {
static public function getById($value) {
echo "I'm " . __CLASS__ . "\n";
}
}
/* New Cache Mapper */
class DataMapper_Cache {
static public function getById($value) {
echo "I'm " . __CLASS__ . "\n";
}
}
// Running before rename and adopt
DataMapper::getById(12345);
// Do the renaming and adopt
runkit_method_rename('DataMapper', 'getById', 'getById_old');
runkit_class_adopt('DataMapper','DataMapper_Cache');
// Run the same code..
DataMapper::getById(12345);
?>
Output:
I'm DataMapper
I'm DataMapper_Cache

Categories