Some (advanced) questions around PHP classes
I've got a couple of questions about PHP classes but they cannot be asked separately, so here I go:
I have a CMS that I am building and at the core of it there is my baseClass, logger, DB and modules classes. DB and logger are static classes so I can use them from within other objects. There are a lot of module classes that are loaded into the baseClass with:
class baseClass {
private $_modules;
public function __construct() {
logger::log("instance created.");
$this->_modules = Array()
}
public function __destruct() {
foreach($this->_modules as $name) $this->unloadModule($name);
logger::log("instance destroyed.");
}
public function loadModule($name) {
if (class_exists($name)) {
if (!isset($this->$name)) {
$this->_modules[] = $name;
$this->$name = new $name();
logger::log("module '$name' loaded.");
}
else logger::log("module '$name' already loaded.", 2);
}
else logger::log("module '$name' does not exist.", 3);
}
public function unloadModule($name) {
if (isset($this->$name)) {
unset($this->$name);
foreach($this->_modules as $id => $mod_name) {
if ($name==$mod_name) {
unset($this->_modules[$id]);
logger::log("module '$name' unloaded.");
}
}
}
else logger::log("module '$name' not loaded.", 2);
}
public function listModules() {
return $this->_modules;
}
}
$instance = new baseClass();
$instance->loadModule('moduleX');
$instance->loadModule('moduleY');
Question 1: Is my loadModule implementation a good idea or you have a better solution. Or is it possible to extend a class the other way around, so I wouldn't have to load the modules, instead their class definition would load themselves into the baseClass like:
class moduleX reverseExtends baseClass {
public function f1();
public function f2();
}
class moduleY reverseExtends baseClass {
public function f3();
public function f4();
}
$instance = new baseClass();
$instance->f1();
$instance->f2();
$instance->f3();
$instance->f4();
Or at least:
$instance->moduleX->f1();
$instance->moduleX->f2();
$instance->moduleY->f3();
$instance->moduleY->f4();
Question 2: For now I am using my logger and DB classes as static classes (I call them with logger:: and DB::) so they are accessible from any context and scope - is it possible to instantiate them and still use them inside my baseClass object but without instantiating them inside the baseClass or setting them up like this inside every class I am going to use it with:
public function __construct() {
global $logger_instance;
$this->logger = $logger_instance;
}
I've tried different methods ranging from bad as passing a reference of the original object inside the baseClass to ugly as using wrapper functions. Using references destroys my logger object when baseClass is unset and is hard to access via modules - $this->base->logger->log() - which in turn requires a reference to the baseClass object ($this->base) and kills my baseClass object on unloadModule... As for the wrapper function - I want to be able to have multiple instances of the logger object, so passing an instance identificator along with the function makes it really ugly and 'untrue' solution.
I need something like:
public function someFuncionInsideSomeClass() {
$logger->('Log something'); //or $logger('Log something') with __invoke()
//$logger here is in the global scope
}
Question 3: I want one module to be able to modify the code of another one. For example I have a database module loaded, then I load a logger module and want the logger module to integrate into the database module code without me having to mess with it's original code. Of course the logger would have to take care of the integration itself (or rather me as the coder). Simple example:
class tagCreator {
public function createTag($tag,$data) {
$tag1 = $tag;
$tag2 = '/'.$tag;
return "<$tag1>$data<$tag2>";
}
}
class styleFunctionality {
public function __construct() {
//if class is loaded (tagCreator) | load class (tagCreator)
//do some magic here to tagCreator
//tagCreator -> createTag -> add argument $style
//tagCreator -> createTag -> $tag1 += ' style="'.$style.'"'
//else issue error tagCreator does not exist
}
}
$instance = new baseClass();
$instance->loadModule('tagCreator');
echo $instance->createTag('span','some string');
// returns <span>some string</span>
$instance->loadModule('styleFunctionality');
echo $instance->createTag('span','some string','font-weight: bold');
// returns <span style="font-weight: bold">some string</span>
Note: I do not necessarily mean function overloading, I might want to change only a bit of code inside a method
Note 2: Calling $instance->createTag() is not an error - I plan on writing some fancy __call() function for baseClass to pick methods from its loaded modules
Sorry for the long topic and thanks for reading this far, I'd even appreciate an answer to only one of my questions :)
For DB and Logger, I would consider using a Singleton pattern.
<?php
class Singleton {
private static $instance;
private function __construct() {
// no-op
}
/**
* Create the instance or return the instance if it has already been created.
*
* @return object
*/
static public function get_instance() {
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
}
?>
In general, you may want to look at the PHP frameworks that require PHP5 such as Kohana. And, as far as patterns and PHP goes, you may want to checkout "PHP Objects, Patterns, and Practice."
Q1 - I would use an autoloader function to load up the classes. You could parse the classname to find out what the location and type of the class you're trying to load (eg - controller, module, plugin etc etc).
In conjunction with __get()
(in your base class) you will be able to call your modules like $instance->moduleX->f1();
if you want.
Q2 - I use the static classes for these kinds of activities as well. Apart from having a globally available variable - with risks of conflicting with your code in other areas - there isn't much else you could do.
Q3 - There's a pattern for something similar to what you're trying to achieve, but for the life of me I can't seem to remember it right now. But how it goes is, you'll need to register your modifier class with your main class. Looking at how you've structured your app, you can get at this by looking at the base class modules array, or by passing the modifier class to your main class ( tagCreator
).
However you decide to do it, you need to modify your createTag()
method to look for loaded modifier classes ( styleFunctionality
) before returning the output. Your modifier classes will of course need some kind of standard interface for your main class to call.
A word of warning, looking at how you're loading your modules into the same instance. You might have conflicts if two classes have the same method defined. I recommend you access them separately like $this->ModuleA->aMethod()
.
上一篇: ASP.NET代码生成器
下一篇: 关于PHP类的一些(高级)问题