I'm developing an MVC framework and I have a problem with making a flexible code/Structure in declaring helper classes
class Controller {
public $helper = [];
public function load_helper($helper) {
require_once(DIR_HELPER . $helper . '.php');
$lc_helper = StrToLower($helper);
$helper_arr[$lc_helper] = new $helper;
$this->helper[$lc_helper] = $helper_arr[$lc_helper];
}
}
//I'm calling the function in my controllers like this
Class Home Extends Controller {
$this->load_helper('Form');
$this->helper['form']-><class function>;
}
I want to call the function like this:
$this->form-><class function>;
I can't use extract for public functions but I've seen frameworks that can do it.
I hope someone has an idea and that someone can understand my question, thanks in advance.
Take a look at the magic __get method. From the documentation:
Overloading in PHP provides means to dynamically "create" properties
and methods. These dynamic entities are processed via magic methods
one can establish in a class for various action types.
The overloading methods are invoked when interacting with properties
or methods that have not been declared or are not visible in the
current scope. The rest of this section will use the terms
"inaccessible properties" and "inaccessible methods" to refer to this
combination of declaration and visibility.
This could be implemented for example this way:
class Controller {
public $helper = [];
public function load_helper($helper) {
require_once(DIR_HELPER . $helper . '.php');
$lc_helper = StrToLower($helper);
$helper_arr[$lc_helper] = new $helper;
$this->helper[$lc_helper] = $helper_arr[$lc_helper];
}
public function __get($property) {
//Load helper if not exists
if (!isset($this->helper[$property])) {
$this->load_helper($property);
}
//Return the helper
return $this->helper[$property];
}
}
Side note:
Controller::$helper and Controller::load_helper() in my understanding should be private or protected instead of public.
Related
In the progress of writing a little framework for a web app I came along some difficulties in making classes communicate with each other.
Environment
I have an abstract class called LizardModule, that should be extended by all the single modules of the web-app. This class has a final protected function registerController(...), that creates a new Object of the type LizardController. This is, as it sounds, based on the idea of MVC. With the final protected function registerFunction(...), modules can register functions for every controller. Those are stored using addFunction(...) on the controller object. Here is what this looks like:
Example Module:
class ModuleOverview extends LizardModule {
protected function setup() {
$this->registerController(
'overview',
'App Overview'
);
$this->registerFunction(
'overview',
'myfunction',
'My Function',
array(&$this, 'theFunctionToCall')
);
}
public function theFunctionToCall() { ... Generate Content ... }
}
Module Class:
class LizardModule {
private $controllers = array();
final public function __construct() { $this->setup(); }
abstract protected function setup();
[...]
final protected function registerController($controllerSlug, $controllerName) {
if (array_key_exists($controllerSlug, $this->controllers))
return false;
$this->controllers[$controllerSlug] = new LizardController($controllerSlug, $controllerName);
}
final protected function registerFunction($controllerSlug, $functionSlug, $functionName, callable $function) {
if (!array_key_exists($controllerSlug, $this->controllers))
return false;
$this->controllers[$controllerSlug]->addFunction($functionSlug, $functionName, $function);
}
}
This results in a lot of objects of type LizardController in different places of the app. To make all of those objects accessable, I created a singleton class LizardRouter, that should hold a reference to all of those controller objects. Therefore, the controller-object registers itself with this singleton class:
Controller Class:
class LizardController {
[...]
private $functions = array();
public function __construct($slug, $name, $menu) {
$this->slug = $slug;
$this->name = $name;
$this->menu = $menu;
LizardRouter::registerController($this);
}
public function addFunction(...) { Tested, this works. }
public function getFunctions() {
return $this->functions;
}
}
Router Class:
final class LizardRouter {
[...]
public static function getControllers() {
return static::getInstance()->controllers;
}
public static function registerController(LizardController $controller) {
static::getInstance()->controllers[] = $controller;
}
}
The Problem
The whole thing works alright for the controllers. In my interface class, I can read out all controllers and print a menu containing their names. The problem is: Whenever I access the controllers functions-array (see controller class) through the controllers-array given by the routing class, I get an empty array. I asume that somewhere a reference is not working and I am passing the actual controller object, before my module-class was able to add the functions to the controllers functions-array. But I can't figure out where exactly the problem lies. Here is an example from my interface class showing the problem:
foreach (LizardRouter::getControllers() as $controller) {
// Allways returns an empty array, even though
// the module added functions to the controller.
$controller->getFunctions();
}
Since this is a very specific case, I guess it is unlikely, that anyone will ever stumble upon the same problem. Anyway; I found the reason for the problem:
Objects are by default passed as reference since PHP5. Variables are by default passed by value.
Arrays are handled like variables, so when I pass an array containing object-references, a new copy of this array is created and passed. Object references added to the array after it was passed are therefore only added to the original array.
The solution i chose was to create my own "array-class" for holding objects. It has nothing more than a private array object, a setter and a getter. Since this custom array class is an object, it is automatically passed by reference. Later I also added some functions to conveniently access the array - a good side-effect.
I'm struggling to find a correct approach to pass data between classes, which do not directly call each other, and are only related through a parent class (which I now use, but I consider it a dirty workaround rather than anything near a solution).
I have 3 classes both able to read input and write output, and based on configuration I set one to read, another one to write. It may even be the same class, they all share a parent class, but they are always two separate instances called from a controller class.
Currently I use this sort of functionality:
class daddy {
public static $data;
}
class son extends daddy {
public function setData() {
parent::$data = "candy";
}
}
class daughter extends daddy {
public function getData() {
echo parent::$data;
}
}
while($processALineFromConfig)
$son = new son;
$son->setData();
$daughter = new daughter;
$daughter->getData();
daddy::$data = null; //reset the data, in the actual code $daughter does that in parent::
}
Instantination of these classes runs in a loop, therefore I always need to reset the data after $daughter receives them, 'cos otherwise it would stay there for another pass through the loop.
I'm absolutely sure it's not how class inheritance is supposed to be used, however I'm struggling to find a real solution. It only makes sense the data should be stored in the controller which calls these classes, not the parent, but I already use return values in the setter and getter functions, and I am not passing a variable by reference to store it there to these functions 'cos I have optional parameters there and I'm trying to keep the code clean.
What would be the correct approach to pass data through the controller then?
Thanks!
The best option would be for two object share some other, third object. This would be the class for "third object" which will ensure the exchage:
class Messenger
{
private $data;
public function store($value)
{
$this->data = $value;
}
public function fetch()
{
return $this->data;
}
}
Then a class for both instance, that will need to share some state:
class FooBar
{
private $messenger;
private $name = 'Nobody';
public function __construct($messenger, $name)
{
$this->messenger = messenger;
$this->name = $name;
}
public function setSharedParam($value)
{
$this->messenger->store($value);
}
public function getSharedParameter()
{
return $this->name . ': ' . $this->messenger->fetch();
}
}
You utilize the classes like this:
$conduit = new Messenger;
$john = new FooBar($conduit, 'Crichton');
$dominar = new FooBar($conduit, 'Rygel');
$dominar->setSharedParameter('crackers');
echo $john->getSharedParameter();
// Crichton: crackers
Basically, they both are accessing the same object. This also can be further expanded by making both instance to observe the instance of Messenger.
I want to create an instance of a class and call a method on that instance, in a single line of code.
PHP won't allow calling a method on a regular constructor:
new Foo()->set_sth(); // Outputs an error.
So I'm using, if I can call it that, a static constructor:
Foo::construct()->set_sth();
Here's my question:
Is using static constructors like that considered a good practice and if yes, how would you recommend naming the methods for these static constructors?
I've been hesitating over the following options:
Foo::construct();
Foo::create();
Foo::factory()
Foo::Foo();
constructor::Foo();
Static constructors (or "named constructors") are only beneficial to prove an intention, as #koen says.
Since 5.4 though, someting called "dereferencing" appeared, which permits you to inline class instantiation directly with a method call.
(new MyClass($arg1))->doSomething(); // works with newer versions of php
So, static constructors are only useful if you have multiple ways to instantiate your objects.
If you have only one (always the same type of arguments and number of args), there is no need for static constructors.
But if you have multiple ways of instantiations, then static constructors are very useful, as it avoids to pollute your main constructor with useless argument checking, weakening languages constraints.
Example:
<?php
class Duration
{
private $start;
private $end;
// or public depending if you still want to allow direct instantiation
private function __construct($startTimeStamp = null, $endTimestamp = null)
{
$this->start = $startTimestamp;
$this->end = $endTimestamp;
}
public static function fromDateTime(\DateTime $start, \DateTime $end)
{
return new self($start->format('U'), $end->format('U'));
}
public static function oneDayStartingToday()
{
$day = new self;
$day->start = time();
$day->end = (new \DateTimeImmutable)->modify('+1 day')->format('U');
return $day;
}
}
As you can see in oneDayStartingToday, the static method can access private fields of the instance! Crazy isn't it ? :)
For a better explanation, see http://verraes.net/2014/06/named-constructors-in-php/
The naming of any method should be with intention revealing names. I can't tell what 'Foo::factory' does. Try to build to a higher level language:
User::with100StartingPoints();
This would be the same as:
$user = new User();
$user->setPointsTo(100);
You could also easily test whether User::with100StartingPoints() is equal to this.
If you don't need a reference to the newly constructed Foo, why don't you simply make set_sth a static function (and have it create a new Foo internally if required)?
If you do need to get hold of the reference, how would you do it? return $this in set_sth? But then set_sth can be made into a factory function anyway.
The only situation I can think of is if you want to call chainable methods (like in a fluent interface) on a newly constructed instance all in one expression. Is that what you are trying to do?
Anyway, you can use a general-purpose factory function for all types of objects, e.g.
function create_new($type) {
return new $type;
}
create_new('Foo')->set_sth();
It's probably not quite a best practice, but you could use the fact that functions and classes have two different namespaces : you can have a function that have the same name as a class.
This allows one to write this kind of code, for example :
function MyClass() {
return new MyClass();
}
class MyClass {
public function __construct() {
$this->a = "plop";
}
public function test() {
echo $this->a;
}
protected $a;
}
Note that I have defined a function called MyClass, and a class with the same name.
Then, you can write this :
MyClass()->test();
Which will work perfectly, and not get you any error -- here, you'll get the following output :
plop
Addition to Jon's answer: To allow constructor arguments use the following:
function create($type) {
$args = func_get_args();
$reflect = new ReflectionClass(array_shift($args));
return $reflect->newInstanceArgs($args);
}
create('Foo', 'some', 'args')->bar();
Documentation: ReflectionClass->newInstanceArgs
These are called creation methods, and I typically name them createXXX() such as createById() or createEmptyCatalog(). Not only do they provide a nice way to reveal the different intentions of an object's constructors, but they enable immediate method chaining in a fluent interface.
echo Html_Img::createStatic('/images/missing-image.jpg')
->setSize(60, 90)
->setTitle('No image for this article')
->setClass('article-thumbnail');
Propel uses a static method "create". I'd go with that. This method makes the code easier to test rather than just using static methods to perform business logic.
<?php
class MyClass
{
public static function create()
{
return new MyClass();
}
public function myMethod()
{
}
}
Besides, you can also pass parameters to the constructor. For instance:
<?php
class MyClass
{
public function __construct($param1, $param2)
{
//initialization using params
}
public static function create($param1, $param2)
{
return new MyClass($param1, $param2); // return new self($param1, $param2); alternative ;)
}
public function myMethod()
{
}
}
In either case, you'd be able to invoke myMethod right after the create method
<?php
MyClass::create()->myMethod();
// or
MyClass::create($param1, $param2)->myMethod();
A bit late to the party but I think this might help.
class MyClass
{
function __construct() {
// constructor initializations here
}
public static myMethod($set = null) {
// if myclass is not instantiated
if (is_null($set)) {
// return new instance
$d = new MyClass();
return $d->Up('s');
} else {
// myclass is instantiated
// my method code goes here
}
}
}
this can then be used as
$result = MyClass::myMethod();
optional parameters can be passed through either the __constructor or myMethod.
This is my first post and I hope I got the gimmicks right
public function getHelperInstance()
{
$user = new Helper();
$user->set($result['data']);
return $user;
}
I am calling getHelper() class multiple times and if $user is not empty than am calling getHelperInstance(), now in my case getHelperInstance() always creates a new instance of Helper() class and so every time I call getHelperInstance() function am creating a new instance of Helper() so is there any way where can I can just create one instance of Helper() and use it multiple times instead of creating a new instance everytime. Any suggestions !!!
public function getHelper()
{
$user = array();
if (!empty($user))
{
$user = $this->getHelperInstance();
}
return $user;
}
Here is what Erich Gamma, one of the Singleton pattern's inventors, has to say about it:
"I'm in favor of dropping Singleton. Its use is almost always a design smell"
So, instead of a Singleton, I suggest to use Dependency Injection.
Create the Helper instance before you create what is $this. Then set the helper instance to the $this instance from the outside, either through a setter method or through the constructor.
As an alternative, create a Helper broker that knows how to instantiate helpers by name and pass that to the $this instance:
class HelperBroker
{
protected $helpers = array();
public function getHelper($name)
{
// check if we have a helper of this name already
if(!array_key_exists($name, $this->helpers)) {
// create helper and store for later subsequent calls
$this->helpers[$name] = new $name;
}
return $this->helpers[$name];
}
}
This way you can lazy load helpers as needed and will never get a second instance, without having to use Singleton. Pass an instance of the broker to every class that needs to use helpers.
Example with a single helper
$helper = new Helper;
$someClass = new Something($helper);
and
class Something
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function useHelper()
{
$return = $this->helper->doSomethingHelpful();
}
}
Inside $something you can now store and access the helper instance directly. You don't need to instantiate anything. In fact, $something doesn't even have to bother about how a helper is instantiated, because we give $something everything it might need upfront.
Now, if you want to use more than one helper in $someClass, you'd use the same principle:
$helper1 = new Helper;
$helper2 = new OtherHelper;
$something = new Something($helper1, $helper2);
This list will get rather long the more dependencies you insert upfront. We might not want to instantiate all helpers all the time as well. That's where the HelperBroker comes into play. Instead of passing every helper as a ready instance to the $something, we inject an object that knows how to create helpers and also keeps track of them.
$broker = new HelperBroker;
$something = new Something($broker);
and
class Something
{
protected $helperBroker;
public function __construct($broker)
{
$this->helperBroker = $broker;
}
public function doSomethingHelpful()
{
$return = $this->getHelper('foo')->doSomethingHelpful();
}
public function doSomethingElse()
{
$return = $this->getHelper('bar')->doSomethingElse();
}
}
Now $something can get the helpers it needs, when it needs them from the broker. In addition, any class that needs to access helpers does now no longer need to bother about how to create the helper, because this logic is encapsulated inside the broker.
$broker = new HelperBroker;
$something = new Something($broker);
$other = new Other($broker);
The broker also makes sure that you only have one helper instance, because when a helper was instantiated, it is stored inside the broker and returned on subsequent calls. This solves your initial problem, that you don't want to reinstance any helpers. It also doesn't force your helpers to know anything about how to manage themselves in the global state, like the Singleton does. Instead you helpers can concentrate on their responsibility: helping. That's clean, simple and reusable.
It sounds like you are interested in the singleton pattern. If you are using PHP5+, you should be able to take advantage of PHP's OOP stuff.
Here's an article on how to implement a singleton in php4. (But I would strongly suggest updating to php5 if that is an option at all)
class Singleton {
function Singleton() {
// Perform object initialization here.
}
function &getInstance() {
static $instance = null;
if (null === $instance) {
$instance = new Singleton();
}
return $instance;
}
}
PHP 4 Singleton Pattern
FYI, if you have any control over which PHP version you use you really should migrate to PHP 5.
I have a PHP web application built with CodeIgniter MVC framework. I wish to test various controller classes. I'm using Toast for unit testing. My controllers have no state, everything they process is either saved into session or passed to view to display. Creating a mock session object and testing whether that works properly is straightforward (just create a mock object and inject it with $controller->session = $mock).
What I don't know, is how to work with views. In CodeIgniter, views are loaded as:
$this->load->view($view_name, $vars, $return);
Since I don't want to alter CI code, I though I could create a mock Loader and replace the original. And here lies the problem, I cannot find a way to derive a new class from CI_Loader.
If I don't include the system/libraries/Loader.php file, the class CI_Loader is undefined and I cannot inherit from it:
class Loader_mock extends CI_Loader
If I do include the file (using require_once), I get the error:
Cannot redeclare class CI_Loader
Looks like CI code itself does not use require_once from whatever reason.
Does anyone here have experience with unit testing CodeIgniter powered applications?
Edit: I tried to inject a real loader object at run-time into a mock class, and redirect all calls and variables with __call, __set, __get, __isset and __unset. But, it does not seem to work (I don't get any errors though, just no output, i.e. blank page from Toast). Here's the code:
class Loader_mock
{
public $real_loader;
public $varijable = array();
public function Loader_mock($real)
{
$this->real_loader = $real;
}
public function __call($name, $arguments)
{
return $this->real_loader->$name($arguments);
}
public function __set($name, $value)
{
return $this->real_loader->$name = $value;
}
public function __isset($name)
{
return isset($this->real_loader->$name);
}
public function __unset($name)
{
unset($this->loader->$name);
}
public function __get($name)
{
return $this->real_loader->$name;
}
public function view($view, $vars = array(), $return = FALSE)
{
$varijable = $vars;
}
}
Alternatively, you could do this:
$CI =& get_instance();
$CI = load_class('Loader');
class MockLoader extends CI_Loader
{
function __construct()
{
parent::__construct();
}
}
Then in your controller do $this->load = new MockLoader().
My current solution is to alter the CodeIgniter code to use require_once instead of require. Here's the patch I'm going to send to CI developers in case someone needs to do the same until they accept it:
diff --git a/system/codeigniter/Common.php b/system/codeigniter/Common.php
--- a/system/codeigniter/Common.php
+++ b/system/codeigniter/Common.php
## -100,20 +100,20 ## function &load_class($class, $instantiate = TRUE)
// folder we'll load the native class from the system/libraries folder.
if (file_exists(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT))
{
- require(BASEPATH.'libraries/'.$class.EXT);
- require(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.config_item('subclass_prefix').$class.EXT);
$is_subclass = TRUE;
}
else
{
if (file_exists(APPPATH.'libraries/'.$class.EXT))
{
- require(APPPATH.'libraries/'.$class.EXT);
+ require_once(APPPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
else
{
- require(BASEPATH.'libraries/'.$class.EXT);
+ require_once(BASEPATH.'libraries/'.$class.EXT);
$is_subclass = FALSE;
}
}
I can't help you much with the testing, but I can help you extend the CI library.
You can create your own MY_Loader class inside /application/libraries/MY_Loader.php.
<?php
class MY_Loader extends CI_Loader {
function view($view, $vars = array(), $return = FALSE) {
echo 'My custom code goes here';
}
}
CodeIgniter will see this automatically. Just put in the functions you want to replace in the original library. Everything else will use the original.
For more info check out the CI manual page for creating core system classes.
I'm impressed by the code you are trying to use.
So now I'm wondering how the 'Hooks' class of CodeIgniter could be of any help to your problem?
http://codeigniter.com/user_guide/general/hooks.html
Kind regards,
Rein Groot
The controller should not contain domain logic, so unit tests make no sense here.
Instead I would test the controllers and views with acceptance tests.