PHP autoloading helpers inside a class - php

I currently have a manual method for registering helpers into my base connection class which goes pretty much as follows:
class db_con
{
// define the usual suspect properties..
public $helpers; // helper objects will get registered here..
public function __construct()
{
// fire up the connection or die trying
$this->helpers = (object) array();
}
public function __destruct()
{
$this->helpers = null;
$this->connection = null;
}
// $name = desired handle for the helper
// $helper = name of class to be registered
public function register_helper($name, $helper)
{
if(!isset($this->helpers->$name, $helper))
{
// tack on a helper..
$this->helpers->$name = new $helper($this);
}
}
// generic DB interaction methods follow..
}
Then a helper class such as..
class user_auth
{
public function __construct($connection){ }
public function __destruct(){ }
public function user_method($somevars)
{
// do something with user details
}
}
So after creating the $connection object, i would then manually register a helper like so:
$connection->register_helper('users', 'user_auth');
Now my question is, could I somehow autoload helper classes inside the base connection class? (within the register_helper() method or similar) Or am I limited to loading them manually or via an external autoloader of some form?
My apologies if this question has been answered elsewhere, but I just haven't found it (not for lack of trying) and I haven't any real experience autoloading anything yet.
Any help or pointers greatly appreciated, thanks in advance! :)
EDIT: As per Vic's suggestion this is the working solution I came up with for the register method..
public function register_handlers()
{
$handler_dir = 'path/to/database/handlers/';
foreach (glob($handler_dir . '*.class.php') as $handler_file)
{
$handler_bits = explode('.', basename($handler_file));
$handler = $handler_bits[0];
if(!class_exists($handler, false))
{
include_once $handler_file;
if(!isset($this->handle->$handler, $handler))
{
$this->handle->$handler = new $handler($this);
}
}
}
}
This appears to include and register the objects absolutely fine for now, whether this solution is a "good" one or not, I can't know without more input or testing.

The code could look something like below, but why would you need this?
public function register_helper($name, $helper)
{
if(!isset($this->helpers->$name, $helper))
{
$this->load_class($helper);
// tack on a helper..
$this->helpers->$name = new $helper($this);
}
}
private function load_class($class)
{
if( !class_exists($class, false) ) {
$class_file = PATH_SOME_WHERE . $class . '.php';
require $class_file;
}
}

Related

Use instance of child class instead of parent class given a condition

I'm working on breaking up a large, monolithic class into several subclasses, but it's too much to do all at once so I'm looking to split them out one by one over several releases as time permits. It's an authentication class that authorizes some channel, so currently it looks like this:
$auth = new Auth($user, $data);
$output = $auth->authChannel($channelName);
Inside Auth, it basically looks like this:
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction();
} else {
// invalid channel
}
}
So self::CHANNEL_AUTH_FUNCTIONS is basically ['channelA' => 'authChannelA', 'channelB' => 'authChannelB'], etc., and all those functions are in this one class.
Now what I want to do, one at a time, is if $legacyChannel => callLegacyFunction() / else $newChannel => instantiate its own class and call auth().
So I put Auth.php into its own namespace and have the new Channel.php class in that same namespace. And Channel extends Auth.
Currently I have this:
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel];
if ($authFunction) {
return $this->$authFunction();
} else {
$authClassName = __NAMESPACE__ . '\\' . ucwords($channel);
$authClass = new $authClassName($user, $data);
return $authClass->auth();
}
} else {
// invalid channel
}
}
Is there a better way to do this? Currently it seems a bit wasteful since two different objects are created and the setUserData() function for example would need to be called again I believe. I'm also wondering if there's a better way to get the dynamic class name other than through __NAMESPACE__ . / . $className.
You'll have to work quite a bit until that code starts looking better. I'll try to suggest as few changes as possible, to make "migration" as painless as possible, although you are a few steps removed from a clean design.
To start with, you can create an AuthStrategyInterface for your new authentication classes.
interface AuthStrategyInterface
{
public function supports(string $channel): bool;
public function auth($user, $data);
}
Each of your new authentication classes should implement this interface. The method supports($channel) is easy enough to understand: if a authentication class can deal with certain channel, it should return true.
Your Auth class would need a way to get these strategies injected. Usually you would do that in the constructor... but to leave your API unchanged we'll just create a setter method for that.
When executing authChannel(), it will first check on the injected strategies to see if any supports the used $channel, and use that if possible. If not, goes back to check your old implementations.
This way you do not need to touch any of the old code as you add new authentication strategies. As you add new implementations, you are gradually strangling the legacy system. At one point no of the old implementations are used, and you can move on to a new code refactoring phase.
class Auth {
private iterable $strategies = [];
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function setAuthStrategies(iterable $strategies)
{
$this->strategies = $strategies;
}
public function authChannel($channel)
{
$this->setUserData();
// check if any of the new strategies supports
foreach ($this->strategies as $strategy) {
if ($strategy->supports($channel) {
return $strategy->auth($this->user, $this->data);
}
}
// check "legacy" authentication methods.
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction($this->user, $this->data);
}
// no valid authentication method
return false;
}
}
To use it, you would do something like this:
$fooAuthStrategy = new FooAuthStrategy();
$barAuthStrategy = new BarAuthStrategy();
$bazAuthStrategy = new BazAuthStrategy();
$auth = new Auth($user, $data);
$auth->setAuthStrategies(
[
$fooAuthStrategy,
$barAuthStrategy,
bazAuthStrategy
]
);
$auth->authChannel($channel);
The specifics would change according to how exactly your application is set-up, but something like this would take you further in a good direction than your current approach.
I don't know if I understood the question correctly, but you couldn't do it like that?
public function authChannel($channel)
{
$this->setUserData();
if (!isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
// Invalid channel
return;
}
return self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel]
? $this->$authFunction()
: parent::auth();
}

Using $config array in MVC classes

I've done quite a bit of reading on this and a lot of people are saying I should be using a singleton class. I was thinking of writing a "Config" class which would include a "config.php" file on __construct and loop through the $config array values and place them into $this->values...
But then I read more and more about how singletons should never be used. So my question is - what is the best approach for this? I want to have a configuration file, for organization purposes, that contains all of my $config variables. I do not want to be hardcoding things such as database usernames and passwords directly into methods, for the purpose of flexibility and whatnot.
For example, I use the following code in an MVC project I am working on:
public/index.php
<?php
include '../app/bootstrap.php';
?>
app/bootstrap.php
<?php
session_start();
function __autoload ($class) {
$file = '../app/'.str_replace('_', '/', strtolower($class)).'.php';
if (file_exists($file)) {
include $file;
}
else {
die($file.' not found');
}
}
$router = new lib_router();
?>
app/lib/router.php
<?php
class lib_router {
private $controller = 'controller_home'; // default controller
private $method = 'index'; // default method
private $params = array();
public function __construct () {
$this->getUrl()->route();
}
private function getUrl () {
if (isset($_GET['url'])) {
$url = trim($_GET['url'], '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
$this->controller = isset($url[0]) ? 'controller_'.ucwords($url[0]) : $this->controller;
$this->method = isset($url[1]) ? $url[1] : $this->method;
unset($url[0], $url[1]);
$this->params = array_values($url);
}
return $this;
}
public function route () {
if (class_exists($this->controller)) {
if (method_exists($this->controller, $this->method)) {
call_user_func(array(new $this->controller, $this->method), $this->params);
}
else {
die('Method '.$this->method.' does not exist');
}
}
else {
die('Class '.$this->controller.' does not exist');
}
}
}
?>
Now let's say I visit http://localhost/myproject/lead/test
It is going to call the controller_lead class and the test method within.
Here is the code for app/controller/lead
<?php
class controller_lead extends controller_base {
public function test ($params) {
echo "lead test works!";
}
}
?>
app/controller/base
<?php
class controller_base {
private $db;
private $model;
public function __construct () {
$this->connect()->getModel();
}
private function connect () {
//$this->db = new PDO($config['db_type'] . ':host=' . $config['db_host'] . ';dbname=' . $config['db_name']. ', $config['db_user'], $config['db_pass'], $options);
return $this;
}
private function getModel () {
$model = str_replace('controller', 'model', get_class($this));
$this->model = new $model($this->db);
}
}
?>
This is where I run into the issue. As you can see, the connect method is going to try and create a new PDO object. Now how am I going to inject this $config variable, given all the other code I just provided?
My options appear to be:
Use a singleton (bad)
Use a global (worse)
Include config.php in bootstrap.php, and inject it throughout multiple classes (Why should I inject this into my lib_router class when lib_router has absolutely nothing to do with the database? This sounds like terrible practice.)
What other option do I have? I don't want to do any of those 3 things...
Any help would be greatly appreciated.
I ended up including a config file in my bootstrap which simply contained constants, and used the constants.
I ended up including a config file in my bootstrap which simply contained constants, and used the constants.
That was the same case for me - required the file in Model. The file based approach was the best fit since it has some benefits: you can .gitignore, set custom permission set, all your parameters are stored centrally, etc.
However, If the config file contains DB connection parameters only, I prefered to require the config file in Model only. Maybe, you could also break down into multiple, more specific config files and require them where necessary.
public function __construct()
{
if (self::$handle === FALSE)
{
$db = array();
require APP_DIR.DIR_SEP.'system'.DIR_SEP.'config'.DIR_SEP.'Database.php';
if (!empty($db))
{
$this->connect($db['db_host'], $db['db_user'], $db['db_password'], $db['db_name']);
}
else
{
Error::throw_error('Abimo Model : No database config found');
}
}
}

Assign functions from another file to a Class

I am trying to add functions to class from a separate file, I wonder if this could be possible!
$mClass = new MyClass();
$mClass->new_Functions[0](10); // Is there a way to have it in this form?
class myClass
{
private $Pvar = 5;
$new_Fcuntions;
function __construct()
{
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
}
}
[additional.functions.php] file
function operate($y)
{
return $this->Pvar * $y;
}
----- Edited ------- as it wasn't clear!
"additional.functions.php" is a module and there will be multiple modules to be added to the application, and every module could have more than single function and modules could call one another!
additional.functions.php [module file]
function operate($y)
{
return $this->Pvar * $y;
}
function do-more($foo)
{
return $this->operate(20) + $foo;
}
another.functions.php [another module]
function do-another($foo)
{
return $this->do-more(30) - $foo;
}
function add($foo, $bar)
{
return $foo + $bar;
}
appreciate every participation, its been a while since I am trying to maneuver around with it!
Is this possible or should I give up!
It looks to me like you are looking for Traits, which are a new feature as of PHP 5.4.0. Using traits, you can have snippets of code "mixed in" to other classes, a concept known as "horizontal reuse".
If you are not looking for traits, it's possible that you could do what you wanted with Runkit, however I would suggest staying as far away from it as possible, if you are not genuinely interested in PHP internals as well.
In any event, whatever you are trying to do is very interesting
I got it to work with dependency injection. The pvar has to be public or create a __get method to return the private variable. I also used the function name because it seems cleaner to me to use it via name rather than it's position in the list but if you want to keep that then just put $key where you see $value from the line: $this->function_list[$value] = ...
function operate($y, $that)
{
return $that->Pvar * $y;
}
class Example {
public $function_list = array();
private $Pvar = 5;
public function __construct()
{
$list = get_defined_functions();
$that = $this;
foreach ($list['user'] as $key => $value) {
$this->function_list[$value] = function() use ($value, $that) {
print call_user_func_array($value, array_merge(func_get_args(), array($that )));
};
}
}
public function __get($key)
{
if (isSet($this->$key)) {
return $this->$key;
} else {
throw new \Exception('Key "'.$key.'" does not exist');
}
}
}
$Ex = new Example();
$Ex->function_list['operate'](10);
If you want to extend MyClass from your modules (and not to initialize it, like in your example code), than you could do it in a way like this:
<?php
namespace modules\MyModuleA;
class MyClassExtension
{
private $MyObject;
public function __construct(\MyClass $MyObject)
{
$this->MyObject = $MyObject;
}
public function doSomething($anyParameter)
{
return $this->MyObject->doSomethingElse($anyParameter * 5, 42, 'foo');
}
}
And MyClass:
<?php
class MyClass extends \Extensible
{
// some code
}
abstract class Extensible
{
private $extensions = [];
public function extend($extension)
{
$this->extensions[] = $extension;
}
public function __call($methodName, $parameters)
{
foreach ($this->extensions as $Extension) {
if (in_array($methodName, get_class_methods($Extension))
return call_user_func_array([$Extension, $methodName], $parameters);
}
throw new \Exception('Call to undefined method ' . $methodName . '...');
}
public function hasExtension($extensionName)
{
return in_array($this->extensions, $extensionName);
}
}
And put it all together:
<?php
$moduleNames = ['MyModuleA', 'MyModuleB'];
$MyObject = new \MyClass;
foreach ($moduleNames as $moduleName) {
$className = '\\modules\\' . $moduleName . '\\MyClassExtension';
$module = new $className($MyObject);
$MyObject->extend($module);
}
// Now you can call a method, that has been added by MyModuleA:
$MyObject->doSomething(10);
You should add an interface for the extension classes of course...
The problem is: What happens if any code in your application calls a method of $MyObject, that is not there, because the module has not been loaded. You would always have to check if ($MyObject->hasExtension('ModuleA')) { ... }, but, of course, the application shouldn't be aware of any module. So I would not design an application in such a way.
I would suggest to use traits (mix-ins). See PHP reference
If you can have another class in that file instead of file with functions
- the best solution will be Traits
http://php.net/manual/en/language.oop5.traits.php
or using inheritance
If you move that code to class you can avoid a lot of unnecessary code. I mean:
include('additional.functions.php');
$arr = get_defined_functions();
$this->new_Functions = $arr['user'];
// trying to call the function with parameter 10
call_user_func(array($this, $this->new_Functions[0]), 10);
It'll be e.g.:
class myClass extends MyBaseClassWithMyAwesomeFunctions
{
private $Pvar = 5;
}
Maybe this approach helps you:
In the files with the additional functions, don't define named functions, but return a closure, that expects (at least) the object (instance of MyClass) as parameter:
<?php
// additional.functions.php
return function ($myObject) {
$Object->multiplyPvar($myObject->getTheNumber());
$Object->doSomethingElse(42, 'foo');
};
The client, that builds MyClass collects those functions from the files into the array:
<?php
$files = [
'/path/to/my/additional.functions1.php',
'/path/to/my/additional.functions2.php'
];
$initFunctions = [];
foreach ($files as $path)
$initFunctions[] = include $path;
$MyObject = new \MyClass($initFunctions);
The constructor then calls those functions:
<?php
class MyClass
{
public function __construct(array $additionalInitFunctions)
{
foreach ($additionalInitFunctions as $additionalInitFunction)
$additionalInitializerFunction($this); // you can also add parameters of course
}
}
This way the class keeps very well testable as well as the function files. Maybe this could help you in any way. You should never ever think about modifying the internal (private) state of an object directly from any code from outside of the class. This is not testable! Think about writing tests before you implement your code (called "test driven development"). You will see, it is not possible to test a class, if you allow any code outside of that class to modify the internal (private) state of the class instance. And you don't want to have this. If you change some internal implementation detail in your class without breaking the unit test of that class, you will anyways probably break some code in any of your additional.functions.php files and no test will tell you: "Hey: you've broken something right now".

Assigning defaults for Smarty using Object Oriented style

I'm just very slowly starting to sink into object-oriented programming, so please be gentle on me.
I have a custom class for Smarty that was partially borrowed. This is how the only example reflects the basic idea of using it across my current project:
class Template {
function Template() {
global $Smarty;
if (!isset($Smarty)) {
$Smarty = new Smarty;
}
}
public static function display($filename) {
global $Smarty;
if (!isset($Smarty)) {
Template::create();
}
$Smarty->display($filename);
}
Then in the PHP, I use the following to display templates based on the above example:
Template::display('head.tpl');
Template::display('category.tpl');
Template::display('footer.tpl');
I made the following example of code (see below) work across universally, so I wouldn't repeat the above lines (see 3 previous lines) all the time in each PHP file.
I would just like to set, e.g.:
Template::defauls();
that would load:
Template::display('head.tpl');
Template::display('template_name_that_would_correspond_with_php_file_name.tpl');
Template::display('footer.tpl');
As you can see Template::display('category.tpl'); will always be changing based on the PHP file, which name is corresponded with the template name, meaning, if for example, PHP file is named stackoverflow.php then the template for it would be stackoverflow.tpl.
I've tried my solution that have worked fine but I don't like it the way it looks (the way it's structured).
What I did was:
Assigned in config a var and called it $current_page_name (that derives the current PHP page name, like this: basename($_SERVER['PHP_SELF'], ".php"); ), which returned, for e.g.: category.
In PHP file I used Template::defaults($current_page_name);
In my custom Smarty class I added the following:
public static function defaults($template) {
global $Smarty;
global $msg;
global $note;
global $attention;
global $err;
if (!isset($Smarty)) {
Templates::create();
}
Templates::assign('msg', $msg);
Templates::assign('note', $note);
Templates::assign('attention', $attention);
Templates::assign('err', $err);
Templates::display('head.tpl');
Templates::display($template . '.tpl');
Templates::display('footer.tpl');
}
Is there a way to make it more concise and well structured? I know about Code Review but I would like you, guys, to take a good look at it.
This looks like you haven't loaded Smarty, that's why the error happens. You need to start by including Smarty before the class starts. If you follow my other config suggestion you should start by including that one as well.
In you Template class, just add the following function:
function defaults() {
// Don't know if you need the assignes, havn't used Smarty, but if so, insert them here...
Template::display( Config::get('header_template') ); //header_template set in the Config file
Template::display( basename($_SERVER['PHP_SELF'], ".php") . '.tpl' );
Template::display( Config::get('footer_template') ); //footer_template set in the Config file
}
Now you should be able to use it in any file:
$template = new Template();
$template->defaults();
EDIT:
A singleton is in every sense the same as a global, that will keep your same problem.
But your problem is that if you try to use one of the Template's static functions you are in the "static" mode, which means the constructor have not been run. And Smarty has not been assigned. If you want to go this road, you can do one of two thinks:
Make the Template a real singleton, meaning set the constructor to private add a function getInstance, that returns a instance of the class, and then use that object to call the functions in it (which should not be static), or
Make all those static functions check if smarty is set, and if it's not, create a new instance of smarty, otherwise use the one that already is instantiated to run its function.
EDIT 2:
Here's the proper way to make a singleton:
class Singleton {
private static $instance = null;
// private static $smarty = null;
private function __construct() {
//self::$smarty = new Smarty();
}
public static function getInstance() {
if( self::$instance === null ) {
self::$instance = self();
}
return self::$instance;
}
public function doSomething() {
//self::$smarty->doSomething();
}
}
It's used like this:
$singleton = Singletong::getInstance();
$singleton->doSomething();
I commented out the things you probably want do to to make this a singleton wrapper around a singleton Smarty object. Hope this helps.
EDIT 3:
Here's a working copy of your code:
class Template {
private static $smarty_instance;
private static $template_instance;
private function Template() {
self::$smarty_instance = new Smarty();
$this->create();
}
public static function getInstance() {
if( ! isset( self::$template_instance ) ) {
self::$template_instance = new self();
}
return self::$template_instance;
}
private function create() {
self::$smarty_instance->compile_check = true;
self::$smarty_instance->debugging = false;
self::$smarty_instance->compile_dir = "/home/docs/public_html/domain.org/tmp/tpls";
self::$smarty_instance->template_dir = "/home/docs/public_html/domain.org";
return true;
}
public function setType($type) {
self::$smarty_instance->type = $type;
}
public function assign($var, $value) {
self::$smarty_instance->assign($var, $value);
}
public function display($filename) {
self::$smarty_instance->display($filename);
}
public function fetch($filename) {
return self::$smarty_instance->fetch($filename);
}
public function defaults($filename) {
global $user_message;
global $user_notification;
global $user_attention;
global $user_error;
self::$smarty_instance->assign('user_message', $user_message);
self::$smarty_instance->assign('user_notification', $user_notification);
self::$smarty_instance->assign('user_attention', $user_attention);
self::$smarty_instance->assign('user_error', $user_error);
self::$smarty_instance->assign('current_page', $filename);
self::$smarty_instance->display('head.tpl');
self::$smarty_instance->display($filename . '.tpl');
self::$smarty_instance->display('footer.tpl');
}
}
When using this function, you should use it like this:
$template = Template::getInstance();
$template->defaults($filename);
Try it now.
You can get current file name in your defaults() function. Use this piece of code:
$currentFile = $_SERVER['REQUEST_URI'];
$parts = explode('/', $currentFile);
$fileName = array_pop($parts);
$viewName = str_replace('.php', '.tpl', $fileName);
$viewName is the name that you need.
This is a quick wrapper I made for Smarty, hope it gives you some ideas
class Template extends Smarty
{
public $template = null;
public $cache = null;
public $compile = null;
public function var($name, $value, $cache)
{
$this->assign($name, $value, $cache);
}
public function render($file, $extends = false)
{
$this->prep();
$pre = null;
$post = null;
if ($extends)
{
$pre = 'extends:';
$post = '|header.tpl|footer.tpl';
}
if ($this->prep())
{
return $this->display($pre . $file . $post);
}
}
public function prep()
{
if (!is_null($this->template))
{
$this->setTemplateDir($this->template);
return true;
}
if (!is_null($this->cache))
{
$this->setCacheDir($this->cache);
}
if (!is_null($this->compile))
{
$this->setCompileDir($this->compile);
return true;
}
return false;
}
}
Then you can use it like this
$view = new Template();
$view->template = 'path/to/template/';
$view->compile = 'path/to/compile/'
$view->cache = 'path/to/cache';
$view->assign('hello', 'world');
// or
$view->var('hello', 'world');
$view->render('index.tpl');
//or
$view->render('index.tpl', true); // for extends functionality
I did this kinda fast, but just to show you the basic ways you can use smarty. In a more complete version you could probably want to check to see if compile dir is writable, or if file templates exist etc.
After trying for few days to solve this simple problem, I have finally came up with working and fully satisfying solution. Remember, I'm just a newby in object-oriented programming and that's the main reason why it took so long.
My main idea was not to use global $Smarty in my initial code that worked already fine. I like to use my Smarty as just simple as entering, e.g.: Template::assign('array', $array). To display defaults, I came up with the trivial solution (read my initial post), where now it can be just used Template::defaults(p()); to display or assign anything that is repeated on each page of your project.
For doing that, I personally stopped on the following fully working solution:
function p() {
return basename($_SERVER['PHP_SELF'], ".php");
}
require('/smarty/Smarty.class.php');
class Template
{
private static $smarty;
static function Smarty()
{
if (!isset(self::$smarty)) {
self::$smarty = new Smarty();
self::Smarty()->compile_check = true;
self::Smarty()->debugging = false;
self::Smarty()->plugins_dir = array(
'/home/docs/public_html/domain.com/smarty/plugins',
'/home/docs/public_html/domain.com/extensions/smarty');
self::Smarty()->compile_dir = "/home/docs/public_html/domain.com/cache";
self::Smarty()->template_dir = "/home/docs/public_html/domain.org";
}
return self::$smarty;
}
public static function setType($type)
{
self::Smarty()->type = $type;
}
public static function assign($var, $value)
{
self::Smarty()->assign($var, $value);
}
public static function display($filename)
{
self::Smarty()->display($filename);
}
public static function fetch($filename)
{
self::Smarty()->fetch($filename);
}
public static function defaults($filename)
{
Template::assign('current_page_name', $filename);
Template::display('head.tpl');
Template::display($filename . '.tpl');
Template::display('footer.tpl');
}
}
Please use it if you like it in your projects but leave comments under this post if you think I could improve it or you have any suggestions.
Initial idea of doing all of that was learning and exercising in writing a PHP code in object-oriented style.

How to include a php and then remove it?

Well, I don't know if this post have the correct title. Feel free to change it.
Ok, this is my scenario:
pluginA.php
function info(){
return "Plugin A";
}
pluginB.php
function info(){
return "Plugin B";
}
Finally, I have a plugin manager that is in charge of import all plugins info to pool array:
Manager.php
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("pluginA.php", "pluginB.php");
foreach ($plugin_names as $name)
{
include_once $name;
$this->pool[] = info();
}
}
}
The problem here is that when I print pool array it only show me the info on the first plugin loaded. I supposed that the file inclusing override the info because it still calling the info() method from the first include.
Is there a way to include the info of both plugins having the info() function with the same name for all plugins files?
Thank you in advance
PS: a fatal cannot redeclare error is never hurled
you can use the dynamic way to create plugin classes
plugin class
class PluginA
{
public function info()
{
return 'info'; //the plugin info
}
}
manager class
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("pluginA", "pluginB"); //Plugin names
foreach ($plugin_names as $name)
{
$file = $name . '.php';
if(file_exists($file))
{
require_once($file); //please use require_once
$class = new $name(/* parameters ... */); //create new plugin object
//now you can call the info method like: $class->info();
}
}
}
}
Are you sure the interpreter isn't choking w/ a fatal error? It should be since you're trying to define the info function twice here.
There are many ways to achieve what you want, one way as in #David's comment above would be to use classes, eg.
class PluginA
{
function info() { return 'Plugin A'; }
}
class PluginB
{
function info() { return 'Plugin B'; }
}
then the Manager class would be something like this:
class Manager
{
protected $pool;
public function loadPluginsInfo()
{
$plugin_names = array("PluginA", "PluginB");
foreach ($plugin_names as $name)
{
include_once $name . '.php';
$this->pool[] = new $name();
}
}
}
Now you have an instance of each plugin class loaded, so to get the info for a plugin you would have $this->pool[0]->info(); for the first plugin. I would recommend going w/ an associative array though so you can easily reference a given plugin. To do this, the assignment to the pool would become:
$this->pool[$name] = new name();
And then you can say:
$this->pool['PluginA']->info();
for example.
There are many other ways to do it. Now that 5.3 is mainstream you could just as easily namespace your groups of functions, but I would still recommend the associative array for the pool as you can reference a plugin in constant time, rather than linear.

Categories