Can we varied a class name during creating a new object? - php

I have two classes written on different php file having same name. I want to load one at a time. For this i search many places .. and i found this piece of code. I want can anybody elaborate what does it doing?
$class= $this->controller.'Controller_';
$controller=new $class($this->registry);
Please explain more if you can ...

You can refer to a class by using a variable. So in this case, a class name is put together using the value of $this->controller and the constant string 'Controller_'. So if $this->controller is 'Foo', $class becomes 'FooController_'.
The second line instantiates an object of this class and passes a value to it's constructor.
So in the end, this code does:
$controller = new FooController_($this->registry);
The advantage is, that the 'Foo' part is variable. So for instance, you could map an url path to a class, so that example.com/foo is handled by a fooController and example.com/bar is handled by a barController.
So this is not what you want. This is loading different classes with different names.
If you have two classes with the same name, you must make sure that you only load the file of the class you want to use. You cannot use two classes with the same name, unless they are in different namespaces. So if you have two files, foo1.php and foo2.php, and each contains a class named foo, then you'll need to include the proper file. But I think this is bad design. Normally you shouldn't have to choose between classes of the same name, unless maybe in a test case, where a unit test can load a mock version of a real class.

Related

Design pattern to return appropriate class depending if autoloader detects file? Factory, Service Locator, etc?

We have a CMS which has standard implementations of Models, Mappers, Forms and DbTables, under the standard Admin_Model_Whatever, Admin_Model_Mapper_Whatever (Zend Framework 1 directory structure).
However, certain projects which share this CMS directory will sometimes need to add additional fields (for example, a custom "color" field for a page view). So in the original CMS directory we have Admin_Model_Page, and now I want to extend this class in the project directory with a class named Project_Model_Page (as well as new page mappers and dbtable classes).
How can I return the correct class from the shared back end implementation? That is, if Project_Class is detected and exists in that certain project's directory, return that, and if not return Admin_Class?
I'd like to avoid doing if class_exists(class) checks every time I need a different form, model, mapper, etc for every create, read, update, delete operation on every page, post, etc etc.
Is there a defined pattern to handle this? I've been looking at Factories, Service Locators, Providers and so on and so forth, but I'm not entirely certain if any of these fit this case - which is simply to have the autoloader detect if a certain class exists in one directory, and if not return a class from a default path.
The Factory-Pattern
You already were on the right track. What you might want to take a look at is the Factory-Pattern
By it's definition the Factory Pattern is a way to instantiate objects not by the classes constructor, but by another class or method.
That's how you probably want to roll. By it's simplest implementation the factory that you need to create here needs to take two arguments. The main class that you want to use and the fallback class in case the main class does not exist.
I will later show a few options how to improve the factory so that it will be able to automate things even further.
Setting up everything
For simplicitys sake I will not use an autoloader here, but using one will work just as fine.
Filesystem Structure
-Dependencies
-- DependencyRouter.php
-Fallbacks
-- FallbackRouter.php
-Interfaces
-- RouterInterface.php
-FallbackFactory.php
-index.php
The Dependencies directory contains the main classes that you want to be instantiated in the first place. The Fallbacks directory contains the corresponding fallback class in case the main class can not be instantiated.
Since both, objects of the main class and the fallback class should be able to be used the same way we will define contracts for them. We do this by creating Interfaces. That's what the last folder is for.
To not take up so much space, the gist for the actual implementations (which is not really part of the question) can be found here.
Let's now have a look on the actual Factory.
<?php
class ClassNotFoundException extends \Exception {}
class FallbackFactory {
public function createInstance( $main, $fallback, $instanceArgs = [] )
{
if( class_exists( $main) )
$reflectionClass = new \ReflectionClass( $main );
else if ( class_exists( $fallback ) )
$reflectionClass = new \ReflectionClass( $fallback );
else
throw new ClassNotFoundException('The Class ' . $main . ' does not exist and neither does the fallback class ' . $fallback);
return $reflectionClass->newInstanceArgs($instanceArgs);
}
}
There is really nothing special going on. First we look if the actual main class does exist. If it does we will store a instance of a ReflectionClass. If it does not we check if the fallback class exist. If it is the case we do the same as before. You could directly instantiate an object but let's use a ReflectionClass here to keep open some neat magic we can add later.
When neither the main class nor the exception class does exist, we should throw an Exception and pass the responsibility for handling the error to the developer.
That's really the whole magic.
index.php
<?php
require_once 'FallbackFactory.php';
require_once 'Interfaces/RouterInterface.php';
require_once 'Dependencies/DependencyRouter.php';
require_once 'Fallbacks/FallbackRouter.php';
$factory = new FallbackFactory();
$router = $factory->createInstance(
'\Dependencies\DependencyRouter',
'\Fallbacks\FallbackRouter'
);
$router->route();
That would be how to use the factory. If the main DependencyRouter could be found the output will be:
I am the main Router
If the DependencyRouter could not be found but the FallbackRouter the output will be:
I am the Fallback router
Otherwise an Exception will be thrown.
Extending the Factory
If you want to make the factory act more dynamically you could do a few things.
1. Make use of namespacing and name your classes consistent
If you want to avoid specifying a Fallback everytime you could name your main and the fallback classes the same but specify a different namespace for them. So you would need to only pass the main class. The fallback class would be determined automatically. e.g
$router = $factory->createInstance(
'\Dependencies\Router',
);
If \Dependencies\Router\ is not present, the factory would automatically look for a \Fallbacks\Router class for example.
2. Automatically resolve Dependencies
At the moment we pass in the constructor arguments as a parameter to the factory method. But by making use of Type Hinting and Reflection you could automagically resolve dependencies.
3. Specifying a Fallback-Hierarchy
Instead of passing one fallback class you could pass an array of multiple classes that all are looked up and resolved if found.

Mock frameworks returns class with different name and type

I'm trying to create a mock to satisfy a typehint with this code (Mockery):
return \Mockery::mock('\Contracts\Helpers\iFileSystemWrapper');
or this (PHPUnit):
return $this->getMock('\Contracts\Helpers\iFileSystemWrapper');
But the mock returned is called Mockery\Mock Object or Mock_iFileSystemWrapper_a5f91049. How am I supposed to type check this when it isn't an instance of what I need at all with either framework?
Why exactly is the mock framework trying to load the real class? If I wanted the real class I would include the real class.
This problem has slowed me down so many times when writing tests I'm about to just toss type hinting out the window and check class names instead, or simply use production objects as mocks are a pain to use.
I just experimented with an existing test of my own, and by changing the interface namespace name from one that exists to one that doesn't exist, I got exactly the same as what you describe (using phpunit). My mock object had the class name Mock_ViewInterface_c755461e. When I change it back to the correct interface name, it works fine.
Therefore I would say that either:
You are trying to use an interface name that doesn't exist (e.g. a typo or missing namespace component).
Your library code isn't being loaded for some reason, e.g. autoloading is not setup correctly in your unit test bootstrap.
You need use a special function to check base class.
Somthing like this:
$mock = $this->getMock('MyClass');
$this->assertInstanceOf('MyClass', $mock);

PHP workaround to extend classes of the same name?

I know extending a class with the same name is not possible, but I was curious if anyone knew of a way to load a class then rename it, so i can later extend it with the original name. Hopefully like something below:
<?php
//function to load and rename Class1 to Class2: does something like this exist?
load_and_rename_class('Class1', 'Class2');
//now i can extend the renamed class and use the original name:
class Class1 extends Class2{
}
?>
EDIT:
Well, I understand that this would be terrible practice in a basic OOP environment where there are large libraries of class files. But i'm using the CakePHP MVC framework and it would make great sense to be able to extend plugin classes in this way since the framework follows a well established naming convention (Model names, view names, controller names, url routes (http://site.com/users), etc).
As of now, to extend a CakePHP plugin (eg: Users plugin) you have to extend all the model, view, and controller classes each with different names by adding a prefix (like AppUsers) then do some more coding to rename the variable names, then you have to code the renamed url routes, etc. etc. to ultimately get back to a 'Users' name convention.
Since the MVC framework code is well organized it would easily make sense in the code if something like the above is able to be implemented.
I'm trying to work out why this would be necessary. I can only think of the following example:
In a context that you have no control over, an object is initialised:
// A class you can't change
class ImmutableClass {
private function __construct() {
$this->myObject = new AnotherImmutableClass();
}
}
$immutable = new ImmutableClass();
// And now you want to call a custom, currently non existing method on myObject
// Because for some reason you need the context that this instance provides
$immutable->myObject->yourCustomMethod();
And so now you want to add methods to AnotherImmutableClass without editing either Immutable class.
This is absolutely impossible.
All you can do from that context is to wrap that object in a decorator, or run a helper function, passing the object.
// Helper function
doSomethingToMyObject($immutable->myObject);
// Or decorator method
$myDecoratedObject = new objectDecorator($immutable->myObject);
$myDecoratedObject->doSomethingToMyObject();
Sorry if I got the wrong end of the stick.
For more information on decorators see this question:
how to implement a decorator in PHP?.
I happen to understand why you would want to do this, and have come up with a way to accomplish what the end goal is. For everyone else, this is an example of what the author may be dealing with...
Through out a CakePHP application you may have references to helper classes (as an example > $this->Form->input();)
Then at some point you may want to add something to that input() function, but still use the Form class name, because it is through out your application. At the same time though you don't want to rewrite the entire Form class, and instead just update small pieces of it. So given that requirement, the way to accomplish it is this...
You do have to copy the existing class out of the Cake core, but you do NOT make any changes to it, and then when ever you upgrade cake you simply make an exact copy to this new directory. (For example copy lib/Cake/View/Helper/FormHelper.php to app/View/Helper/CakeFormHelper.php)
You can then add a new file called app/View/Helper/FormHelper.php and have that FormHelper extend CakeFormHelper, ie.
App::uses('CakeFormHelper', 'View/Helper');
FormHelper extends CakeFormHelper {
// over write the individual pieces of the class here
}

PHP OOP - Require or Extend

I was wondering if there is any major different in the following, and whether one is more 'standard' than the other:
<?php
class Account extends Database {
public function myMethod()
{
// Do something
}
}
?>
or
<?php
require('database.class.php');
class Account {
public function myMethod()
{
// Do something
}
}
?>
Cheers :)
Edit:
This question actually relates to a tutorial series I have been following which describes the above two methods - which didn't make any clear sense.
So thank you for the constructive answers on clearing that one up!
Those are two completely separate language constructs.
Your first example deals with inheritance. Basically, you already have a class called Database, but you want to have a specialized version of that class to handle accounts. Rather than build a brand new Account class and copy/paste all the functionality you already have in your Database class, you simply tell PHP that you want to use the existing Database class as a baseline. You create any account-specific functionality in the new Account class, and anything database-related comes automatically. This is assuming, of course, that you have some way of specifying where the Database class is defined - for example, a require declaration at the top of the class, or an __autoload() or spl_autoload_register() function call defining a way to find and locate the file containing the Database class.
In your second example, your database-related code is completely separated from your Account class. They're completely distinct entities, and if you wanted to do anything database-related in your Account class, you would have to explicitly instantiate a new Database object within that class (or pass it to that class, or one of its functions, as a parameter.
Basically, extends helps define what a class is, whereas require shows where a class definition (or other code) is stored.
Both code snippets aren't even equivalent.
The first declares Account to extend Database, a is-a relation.
In the second code snippet, you are simply saying that you require 'database.class.php' ... and that neither has anything to do with OO, nor defines a is-relation from Account to Database.
Both are completely different in first one class is inherited by another class but in the second one the class is included in your script only.
Means if you extend all the public and protected methods are available in your derived class and you can create object of derived class and can use methods with derived class's object.
But in the second method the class is included in your script and require this class it's own method and work independently.
The first means you create a new class, which has all the functionality of Database class and those you implement.
The second means that you create a new class, but it doesn't have Database functionality since it's not extending it. If you need database access in your Account class, you can create an instance in constructor, or pass already created instance as constructor parameter.
It's hard to say what is more standard, since it depends on what You actually want to achieve.
To put it in most simple terms:-
require or include is structural programming.
extends is object oriented

php extending classes

I'm using a salesforce class called SforceEnterpriseClient. I've referenced that class many places in my application. I want to extend that class to give it the ability to return a single array from a 1 row recordset, right now the record set is about 3 levels deep. There's a few other things I want to do with it as well. I can handle all that.
Everything I've read about classes says that when I extend a class, I need to call the new one as such:
class MySF extends SforceEnterpriseClient {};
$mySforceConnection = new $MySF;
That means in all of my existing code I have to find/replace.
Is it possible to overwrite the parent with the child so I don't have to play the find/replace game?
class SforceEnterpriseClient extends SforceEnterpriseClient {};
$mySforceConnection = new $SforceEnterpriseClient ;
You can probably play some classloading tricks with the magic __autoload() function and removing references to the salesforce file ie. require, require_once, include, include_once; But in the interest of readability and maintainability, you should probably take the long route here and modify all your references to use the subclass.
How about this, in the source file for the class, rename the class (and most likely the constructor as well) then extend the class using something like
class SforceEnterpriseClient extends renamedClass {};
Then rename the file and create a new file with the old name and include the renamed file. Put the code for your extended version in the new file. The final result is that every file that was using the original will see the new version without having to track them all down.
About the only major issue would be what happens when a new version of the class becomes available.
Unfortunately, that would be the only way to do so. You cannot reverse inhertiance. Sorry and good luck!
Kyle
Maybe you do not need to extend the class in this scenario. You extend a class when you want to add new functionality or change existing functionality AND keep the original class intact. Usually this is the way to go. But, if you need to make a change to an existing class and then update all references to the class to refer to the new class why not simply change the class code itself? That way the references would not have to change.
Also have a look at the factory pattern. Normally you should keep class creation and business logic separate.
So when you come across a problem like this, you only have to go and change the factory...
$sfEnterpriseClient = Factory::getSFEnterpriseClient($params);
that way you can avoid 'new' constructs in your business logic, and makes your code more manageable

Categories