Dynamically building/loading a class library in PHP - php

I am fairly new to OO programming...
I am building what will eventually turn out to be a large library of classes to be used throughout my site. Clearly, loading the entire library on every page is a waste of time and energy...
So what I would like to do is require a single "config" php class file on each page, and be able to "call" or "load" other classes as needed - thus extending my class according to my needs.
From what I know, I can't use a function in the config class to simply include() other files, because of scope issues.
What are my options? How do developers usually handle this problem, and what is the most stable?

You can use __autoload() or create an Object Factory that will load the files required when you need them.
As an aside, if you're having scope issues with your library files, you should probably refactor your layout. Most libraries are sets of classes that can be instantiated in any scope.
The following is an example very basic object factory.
class ObjectFactory {
protected $LibraryPath;
function __construct($LibraryPath) {
$this->LibraryPath = $LibraryPath;
}
public function NewObject($Name, $Parameters = array()) {
if (!class_exists($Name) && !$this->LoadClass($Name))
die('Library File `'.$this->LibraryPath.'/'.$Name.'.Class.php` not found.');
return new $Name($this, $Parameters);
}
public function LoadClass($Name) {
$File = $this->LibraryPath.'/'.$Name.'.Class.php'; // Make your own structure.
if (file_exists($File))
return include($File);
else return false;
}
}
// All of your library files should have access to the factory
class LibraryFile {
protected $Factory;
function __construct(&$Factory, $Parameters) {
$this->Factory = $Factory;
}
}

Sound like you want autoload and spl_autoload_register if you are using classes from 3rd party libraries.

Related

PHP OOP: How to bundle multple classes together

I have been reading up on OOP for a while now and it's always confused me but I'm trying to improve on my skills (albeit I'm still starting out with this so please be kind).
I am designing a main app, whose "core" class is $App. My thought is that anywhere in the app, you can do everything from within $App.
For example, I have a $Db class to query the database, $Mail class for sending email, etc. These are all autoloaded using spl_autoload_register.
My question is in regards to actually instantiating them properly. Here's how I'm doing it now:
index.php
require_once('config.php'); // Autoloads classes and some other small stuff
$App = new App;
echo $App->Visitor->getIP();
App.class.php
class App {
public function __construct() {
$this->initialize();
}
private function initialize() {
$this->Visitor = new Visitor($this);
$this->Db = new Db($this);
$this->Mail = new Mail($this);
}
// Then some public methods...
// We are able to use $this->Visitor, $this->Db, etc...
}
Visitor.class.php
class Visitor {
private $App;
public function __construct($App) {
$this->App = $App; // I need access to all of App's methods
}
public function getIP() {
return $_SERVER['REMOTE_ADDR'];
}
}
So the short example above shows how I'm doing this in all my classes. The App class always creates an instance of whatever class we will be using in the entire app in it's constructor.
The "child" classes all [dependency injects?] the $App instance so they can use it's methods.
Am I doing this correctly? It works, but I don't know if there is a more efficient/better practice way of doing this.
The problem with injecting the base class on instantiation is that it makes all of your classes have really ambiguous dependencies. Your Visitor class could rely on a class that parses the $_SERVER superglobal, but I can't tell just by looking at it, it's only dependency is the whole app object.
It's much better to only define the specific dependencies a class needs (usually in the constructor) and create something whose job it is to properly provide these dependencies. This is the "dependency injection" that you were hinting at, and they usually involve some Container that stores all the objects that have been instantiated, ready to inject into new classes.
So your app will have the dependency of a container:
$app = new App(new Container);
And then you can ask the container for dependencies
class App {
/**
* #return App\Visitor
*/
public function getVisitor()
{
if (!$this->visitor) {
$this->visitor = $this->container->get('App\Visitor');
}
return $this->visitor;
}
}
If you use a third party dependency injection library (recommended - even when starting out as designing your own is a bit of a tangent) then the library will usually provide some way to automatically resolve dependencies. It can look at the constructor on your class (that's typehinted correctly, e.g. __construct(Visitor $visitor)) and automatically provide the arguments to the constructor - using the Reflection SPL
I would also recommend if you're starting out with OOP that you first familiarise yourself with PSR-4 and Composer autoloading, as including all of your classes manually is going to become quite a hassle, and doing it the "standard" way at the beginning makes life easier in the future when you will want to switch ;) .

How to automatically load function in PHP

I use this code to load classes
function __autoload($className)
{
$files = dirname(__FILE__).'/public/class/'.$className.'.php';
if(file_exists($files))
{
include_once($files);
}
}
does anyone know how to retrieve the function automatically, too? Thank you.
Autoloader function registered spl_autoload_register() can be used to load classes, but not functions. Wrap your functions in class or classes to utilize autoloading, i.e.
class Utils {
static function foo() {
..
}
}
then call it static way:
Utils::foo();
and you can have it autoloaded when needed. See more infromation on autoloading in PHP manual.
Yes, you can automatically load classes using the autoload feature, but no, you can't do the same for functions.

PHP newbie - PHP Classes autoloading and Securing

I am interested in learning autoloading, but new to PHP.
I am reading the book "PHP in Action" which writes the autoload method like
function __autoload($className) {
include_once __autoloadFilename($className);
}
function __autoloadFilename($className) {
return str_replace('_','/',$className).".php";
}
I want to pack these methods in a class. will it be better to pack them in an abstract class?
Or in normal class and including it in index.php?
How effeciently I can use the autoloading feature?
Thanks
Securing an autoloader, ensure:
That the file you try to load is actually a file. include is pretty much like eval. For example disallow url inclusion via the php configuration.
That the classname is actually a syntactically correct classname. Could be helpful to secure things ;)
You can also white-list namespaces and/or classnames that are appropriate for your concrete autoloader.
Create a class as an autoloader, you must not take the static way, you can just assign any callback with spl_autoload_register, so you can register multiple autoloaders.
Some quickly written autoloader class stub:
class MyAutoloader
{
public function __construct()
{
$this->register();
}
public function register()
{
spl_autoload_register(array($this,'autoload'));
}
public function autoload($classname)
{
if ($this->isInvalidClassName($classname)) return;
$file = $this->getFileForClassName($classname);
if ($this->isInalidFile($file)) return;
require $file; // bail out fatally.
}
...
}
$myAutoloader = new MyAutoloader();
It's up to you. I use a dedicated class called Loader with a method called load(), which I activate with:
spl_autoload_register( 'Utility_Loader::load' );

Instantiate plugins efficiently with observer pattern (php)

I'm implementing the observer pattern to all plugins to interact with my web app. Now, I want to make installing plugins painless (i.e just putting files into a plugin folder) like most web apps do.
For example:
A plugin with the name "Awesome Stuff" that executes code based on events that occur in the Auth class and the Content class would include the following files in the "/plugin" directory.
AwesomeStuffAuth.php
AwesomeStuffContent.php
I've got a solution that's working, but I'm afraid it's not efficient, as it has to cycle through ALL the declared classes before it actually finds what it's looking for.
function __construct() {
//Get files in plugin directory that work on Auth
foreach (glob("plugins/*AuthPlugin.php") as $filename) {
//Include'em
include_once($filename);
}
//Get all declared classes
foreach (get_declared_classes() as $class){
if (substr($class,-10)=='AuthPlugin'){
$obj = new $class;
$this->addObserver($obj);
}
}
}
This is the __construct() method that I use with the Auth class, and other classes would have similar methods.
Is this an efficient way of doing this? Is it worth me connecting to a database to avoid cycling through all the declared classes? How about flat file?
Thanks so much for taking a look at this.
A possible solution would be to use __autoload to catch the moment where the plugin is requested and add it to a list of observers at this moment, based on naming conventions:
function __autoload($class) {
include_once 'plugins/' . $class . '.php';
$observable = substr($class,-16); # E.g. 'Auth'
$observable::addObserverClass($class);
}
then in the Auth class:
class Observable {
static $observer_classes = array();
static function addObserverClass($class) {
self::$observer_classes[] = $class;
}
static function addObservers($self) {
foreach(self::$observer_classes as $class) {
$self->addObserver($class);
}
}
function addObserver($class) {
# Add the Observer.
}
}
class Auth extends Observable {
function __construct() {
self::addObservers($self);
}
}
Haven't tested the code, but you should get the idea. It eliminates both the need to glob and the need to iterate over every declared class each time a class is created.

PHP Including 2 files using same function names give redeclare error?

i'm having trouble with a redeclare error. I can't find a fix for it yet, but basically:
I have 2 files that are modules and they use the same function names like install(), uninstall() etc etc which are used by the system.
When I include both the files at the same time for gathering data, I get a redeclare error. I want it so that I can include both and when the next file is loaded, it just rewrites over the previous function.
Or is there a way I can unset or clear the function? I've tried include, require, require_once etc... No work :(
In PHP it is not possible to overwrite a function that you have previously defined.
So the modules stand in each others way and one module prevents the other from working.
Actually Modules need to make use of the same named functions while they must be able to co-exist next to each other.
That can be done by moving the modules code into classes of their own. One module is one class then.
You can then define an interface with the functions your module classes must provide. As Modules therefore must have a streamlined interface - each module has a install() and uninstall() function for example - just define an object interface at first specifying those needed module functions:
module_definitions.php
interface Module {
public function install();
public function uninstall();
}
mod_Module1.php:
class Module1 implements Module {
public function install() {...}
public function uninstall() {...}
}
mod_Module2.php:
class Module2 implements Module {
public function install() {...}
public function uninstall() {...}
}
After doing so, whenever one of your routines needs to deal with any module, you can make that function require a module:
function module_install(Module $module) {
$module->install();
}
This function will only accept an existing module as a parameter. So you can not use your standard require/include for this but modules need to be instantiated prior use. You can put that into a module loader function as well:
function module_require($moduleName) {
$class = $moduleName;
if (!class_exists($class) {
$file = sprintf('mod_%s.php', $moduleName);
require $file;
if (!class_exists($class)) {
throw new DomainException(sprintf('Invalid Module File %s for Module %s.', $file, $moduleName));
}
}
}
How to access the modules functions then?
The only thing left is now to access the actual module.
You could the create a global array variable containing all modules:
// Define existing modules
$modules = array('Module1', 'Module2');
// Require the modules
array_map('module_require', $modules);
// instantiate each module:
$moduleInstances = array_map(function($module){return new $module;}, $modules);
// map modules name (key) to it's module instance:
$modules = array_combine($modules, $moduleInstances);
// access module by name:
$modules['Module1]->install();
However this has some problems. All modules need to be loaded at once for example, but you might not need to use all modules. Or imagine you would overwrite the global $modules array, all modules would be lost.
To prevent all that and allow more control and easier access to the modules, this can be put into a class of it's own that will take care of all the details. Like a register that knows which modules are loaded or not, registers them as needed.
For the following I assume a module can only exists once. If an object can only exist once this is often called a Singleton. So we'll wrap the management of loading and providing the module by it's name into a class of it's own that deals with the details:
class Modules {
private $modules = array();
private static $instance;
// singleton implementation for Modules manager
private static function getInstance() {
if (null === Modules::$instance) {
Modules::$instance = new Modules;
}
return Modules::$instance;
}
// singleton-like implementation for each Module
public function get($moduleName) {
if (!isset($this->modules[$moduleName]) {
module_require($moduleName);
$newModule = new $moduleName();
if (! $newModule instanceof Module) {
throw new DomainException(sprintf('Not a Module: %s', $moduleName));
}
$this->modules[$moduleName] = $newModule;
}
return $this->modules[$moduleName];
}
// get a module by name
public static function get($moduleName) {
return Modules::getInstance()->get($moduleName);
}
}
Put this class into module_definitions.php as well, which should be always included in your application.
So whenever you need to access a module you can do now by using the static get function with the name of the module:
Modules::get('Module1')->install();
Modules::get('Module2')->install();
No. You have a application design problem.
Rename the second function and call it on the locations you want the second to be used.
You cannot have two functions with the same name in the same scope.
If you have php5.3 or above, namespaces can be the answer: each plugin has its own, so the functions became
\plugin1\install()
\plugin2\install()
et cetera.
You may also wish to create unique classes inside these include files, then have them extend a generic class and use that generic class as a type to anchor to when you want to call up these functions at a higher level. You could also have one overload the other and then when you execute a method in one, it could be passed right on to the next.
Theoretically if you wrap the functions of each file in a separate class then you can call them both without problems. You don't even need to really worry about class state if you call them statically.
You cant use two time the same function name in the same namespace.
You should rename your second function or use namespaces like "Maerlyn" suggest
This problem can be solved by namespaces or/and static class.
Easiest way is to wrap these functions in class with static methods.
After that you'll be able not only to include them both, but also to use autoload-functions and forget about 'include'.
class Class1
{
public static function install()
{}
}
class Class2
{
public static function install()
{}
}
More about namespaces and autoload

Categories