How to autoload extended classes? - php

I am planning to use PHP's autoload function to dynamicly load only class files that are needed. Now this could create a huge mess if every single function has a seperate file, So I am hoping and asking is there a way to have related classes remain in 1 class file and still be auto-loaded
function __autoload($class_name){
include('classes/' . $class_name . '.class.php');
}
Let's say there is a class name animals and then another class named dogs. Dogs class extends animals class, now if I were to call the dogs class but NOT call the animals class, would the animals class file still be loaded?

Have you considered explicit definitions of your class locations? Sometimes it makes a lot of sense to group related classes.
Here is a proven way of handling it.
This code is placed in an auto_prepend_file (or included first)
class Import
{
public static $_AutoLoad = array();
public static $_Imported = array();
public static function Load($sName)
{
if(! isset(self::$_AutoLoad[$sName]))
throw new ImportError("Cannot import module with name '$sName'.");
if(! isset(self::$_Imported[$sName]))
{
self::$_Imported[$sName] = True;
require(self::$_AutoLoad[$sName]);
}
}
public static function Push($sName, $sPath)
{
self::$_AutoLoad[$sName] = $sPath;
}
public static function Auto()
{
function __autoload($sClass)
{
Import::Load($sClass);
}
}
}
And in your bootstrap file, define your classes, and what file they are in.
//Define autoload items
Import::Push('Admin_Layout', App::$Path . '/PHP/Admin_Layout.php');
Import::Push('Admin_Layout_Dialog', App::$Path . '/PHP/Admin_Layout.php');
Import::Push('FileClient', App::$Path . '/PHP/FileClient.php');
And lastly, enable AutoLoad by calling
Import::Auto()
One of the nice things is that you can define "Modules":
Import::Push('MyModule', App::$Path . '/Module/MyModule/Init.php');
And then load them explicitly when needed:
Import::Load('MyModule');
And one of the best parts is you can have additional Import::Push lines in the module, which will define all of its classes at runtime.

if I were to call the dogs class but
NOT call the animals class, would the
animals class file still be loaded?
Yes. When you load an class that extends another class, PHP must load the base class so it knows what it's extending.
re: the idea of storing multiple classes per file: This will not work with the autoload function you provided. One class per file is really the best practice, especially for autoloaded classes.
If you have more than one class in a file, you really shouldn't attempt to autoload any classes from that file.

Yes it will load unless you don't include/require the class file.
You have to always import any files that contain needed PHP code. PHP itself can't guess the name you gave to your class file. Unlike Java for instance, PHP doesn't have any file naming requirements for classes.
The common way so solve this problem is to group related classes in one single file. Creating a "module".

There's a workaround, I've just used it and it seems to work:
Let's assume we have an autoloaded class called animals stored in animals.class.php. In the same file you could have other classes that extends animals
class animals{
static function load(){
return true;
}
}
class dogs extends animals{
}
class cats extends animals{
}
Now... when you need to use your class dogs you need PHP autoload it's parent class (and doing this you're sure it will parse also all the extendend classes inside the same file) so you just have to write:
animals::load();
$fuffy = new dogs();

Related

php - class extending alternative

I have a main class that I add around a dozen child classes to similar to Example #2.
I'm wondering if there is a better way to accomplish this. I know I could do "extends" on classes (Example #1) however in order to be able to have one variable with access to all extended class functions, I'd have to "daisy chain" the extensions and then create a new class reference on the very last extension - this option is not what I'm looking for.
Example #1:
class main {
function _construct(){}
function main_function1(){}
function main_function2(){}
}
class child1 extends main{
function _construct(){}
function child_function1(){}
function child_function2(){}
}
class child2 extends child1 {
function _construct(){}
function child_function3(){}
function child_function4(){}
}
$main = new child2();
$main->child_function1();
$main->child_function2();
$main->child_function3();
$main->child_function4();
Here is what I'm currently doing.
Example #2:
<?php
class main {
function _construct(){}
function main_function1(){}
function main_function2(){}
}
class child1 {
function _construct($main){$this->main = $main;}
function child_function1(){}
function child_function2(){}
}
class child2 {
function _construct($main){$this->main = $main;}
function child_function3(){}
function child_function4(){}
}
$main = new main();
$main->child1 = new child1($main);
$main->child2 = new child2($main);
$main->child1->child_function1();
$main->child1->child_function2();
$main->child2->child_function3();
$main->child2->child_function4();
?>
Is Example #2 the best way to achieve what I'm looking for?
By doing
class child {
function __construct($main){$this->main = $main;}
}
in Example 2 you would pass the $main instance as a property for child in the constructor. In my opinion this would make sense, if $main is a container that provides information for child - which is useful if you would like to avoid having a constructor with many many arguments. According to your naming, it doesn't look like main is a container. Be aware, that you are creating a dependency between child and main then because if you want to instantiate child, you always need an instance of main in advance! What you are also doing in Example 2 is creating circular references:
$main = new main();
$main->child1 = new child1($main);
$main->child2 = new child2($main);
You would be able to call $main->child1->main then which means a high coupling between main and child. So I'd rather not say "go for Example 2".
In your case it rather sounds like child actually is a special case of main, like the relationship between fruit (main) and apple (child). That makes using extends much more reasonable. You seem to be unsure, because you have many child classes extending main. To me this sound just normal, if all the child classes have a similar purpose and share some basic functionalty which is provided by main. But I'm not quite sure what your goal actually is.
Extending classes can break encapsulation, so depending on your classes it might be best to keep your objects separated as in example #2. You could have a loading function to make setting up the main class easier:
class main {
function load($class){
$this->$class = new $class();
}
}
$main = new main();
$main->load('child1');
$main->child1->child_function1();
In php, You have two options. Either (in 2020) inject your Helper Class in another class as suggested in the first answer or in PHP you can use traits.
Traits are similar to classes but their constructors cannot be public.
It's difficult to use Dependency injection on traits but traits can extend existing classes with methods and they can access the parent methods of their respective classes.
With traits anyway you cannot override the parent methods, you can only add the methods to the stack.
Another downside of traits is that, when you use the parent methods of the containing class, type hinting is not well supported in some IDEs. So in order to get type hinting working you'll need some workarounds.

Is it good to separate functions from helper classes?

I'm pretty new to the MVC concept itself but I'm slowly starting to grasp it, and what it is that makes it so useful.
I have in my app different tabs which are levels in a hierarchy (I call them entities), each represented by two classes - a Binding class with all the methods to communicate with the database and a helper class which is just a skeleton of the Entity, both in same file. I wonder if this is the right way to approach the design pattern? I haven't built a Model or a Controller yet because I'm not sure where they'll come in at the moment. Also I'm not interested to use ORM right now.
class CompanyBinding extends EntityBinding {
function __construct() {
parent::__construct();
}
public function get($criteria = array()) {
// method to retrieve an array of DB rows, each represented by the helper class
[...]
return $sth->fetchAll(PDO::FETCH_CLASS, 'Company');
}
public function get_by_id($id) {
// method to retrieve an one single object of the helper class
$sth->setFetchMode(PDO::FETCH_CLASS, 'Company');
return $sth->fetch();
}
}
class Company extends Entity {
public $id;
public $name;
public $email;
public $phone;
// just properties. some classes have a __construct() function to manipulate some fields after retrieval
[...]
}
Do I need a model class now? I was considering the Controller could perform the ->get function and load appropriate class, because the name of the entity is passed from the front-end with AJAX.
By the way I also am really confused. Should the controller absorb all AJAX requests or should I make a file called ajax.php that handles that, and put the Controller class above public_html so users can't access the class file?
Separate the classes into their own files. PHP does a decent job at caching file locations once it opens them, so you don't have to worry about IO. And generally speaking, it is bad practice to put multiple classes in one file. It won't break your application, but it may cause more seasoned developers to break-down into tears. :)
Apart from that, you are fine.

PHP OOP: Get all declared classes which are have extended another parent class

I need to get all declared classes which are have extended another parent class.
So for example...
class ParentClass {
}
class ChildOne extends ParentClass {
}
class ChildTwo extends ParentClass {
}
class ChildThree {
}
I need an array that outputs this:
array('ChildOne', 'ChildTwo')
I'm new to PHP OOP, but based on some Googling, I came up with this solution.
$classes = array();
foreach( get_declared_classes() as $class ) {
if ( is_subclass_of($class, 'ParentClass') ){
array_push($classes, $class);
}
}
What I want to ask is whether this is the best practice to do what I want to do, or is there a better way? The global scope will contain a lot of other classes that isn't a child of ParentClass. Is looping through all declared classes the best way to go?
EDIT (clarification of purpose):
What I want to achieve with this is to instantiate each child class extending the parent class.
I want to do $childone = new ChildOne; $childtwo = new ChildTwo; for every child of ParentClass.
you can try to log the declaration of a class the first time it is loaded.
it suppose you are using autoloading.
if you do not use composer but a custom loader :
It's the easiest way :
$instanciatedChildren = array();//can be a static attribute of the A class
spl_autoload_register(function($class)use($instanciatedChildren){
//here the code you use
if(is_subclass_of($class,'A')){
$instanciatedChildren[] = $class;
}
}
if you use composer :
you can, make a class that extends composer/src/Composer/Autoload/ClassLoader.php
and then override the loadClass method to add the condition given above. and then register your new loader and unregister the old one.
Your solution seems fine, though I'm not sure why you'd do this. There is no easy way in php to say, 'give me all the declared classes of a certain parent class globally' without actually checking globally each declared class. Even if you have a couple hundred classes loaded to loop through, it shouldn't be too heavy as they're all in memory.
If you're trying to just track loaded child classes for a specific parent, why not create a registry that tracks them when they're loaded? You could do this tracking in an autoloader or factory used for the child classes or event as a hack, just by putting something at the top of the class file before the class definition.

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

Class Plugins in PHP?

i just got some more questions while learning PHP, does php implement any built in plugin system?
so the plugin would be able to change the behavior of the core component.
for example something like this works:
include 'core.class.php';
include 'plugin1.class.php';
include 'plugin2.class.php';
new plugin2;
where
core.class.php contains
class core {
public function coremethod1(){
echo 'coremethod1';
}
public function coremethod2(){
echo 'coremethod2';
}
}
plugin1.class.php contains
class plugin1 extends core {
public function coremethod1(){
echo 'plugin1method1';
}
}
plugin2.class.php contains
class plugin2 extends plugin1 {
public function coremethod2(){
echo 'plugin2method2';
}
}
This would be ideal, if not for the problem that now the plugins are dependable on each other, and removing one of the plugins:
include 'core.class.php';
//include 'plugin1.class.php';
include 'plugin2.class.php';
new plugin2;
breaks the whole thing...
are there any proper methods to doing this?
if there are not, them i might consider moving to a different langauge that supports this...
thanks for any help.
edit:
obviously it is my understanding that is lacking, so here is a
attempt at a clarification.
core.class.php contains anything...
plugin1.class.php contains anything...
plugin2.class.php contains anything...
include 'core.class.php';
include 'plugin1.class.php';
include 'plugin2.class.php';
$core = new core;
$core->coremethod1();//outputs plugin2method1
whereas:
include 'core.class.php';
include 'plugin2.class.php';
$core = new core;
$core->coremethod1();//outputs plugin1method1
I'm interested in any implementation, even one not involving classes
for example
include 'core.php';
//does core stuff
include 'core.php';
include 'plugin1';
//does extended core stuff
include 'core.php';
include 'plugin2';
//does extended core stuff
include 'core.php';
include 'plugin2';
include 'plugin1';
//does very extended core stuff
including a file needs to change the application behavior. for it to have any meaning.
I do not know what this is called either, so point me at the proper naming if there is any.
You are misusing the term "plugin". A plugin is generally a package of code that extends or alters the base functionality of a system - to make actual PHP plugins (which in the PHP world are called extensions) you'd be writing C or C++.
What you're describing here is merely including classes or class trees into the current execution for usage. And there is a way to bring them into the current execution context 'automatically', and that's via the autoload system.
If, after you've read the documentation on autoloading, you are still unsure of how to move forward, comment here and I will help you along.
EDIT
Ok, I see what you're after. You can't do exactly what you're after. When you execute new core; an instance of the class core will be returned - you can't modify that at all.
However, if you are willing to modify how you create instances of core, then I think I have something that could work for you, and it might look something like this.
class core {
public function coremethod1(){
echo 'coremethod1';
}
public function coremethod2(){
echo 'coremethod2';
}
/**
* #return core
*/
final public static function create()
{
// listed in order of preference
$plugins = array( 'plugin2', 'plugin1' );
foreach ( $plugins as $plugin )
{
if ( class_exists( $plugin ) )
{
return new $plugin();
}
}
return new self;
}
}
class plugin1 extends core {
public function coremethod1(){
echo 'plugin1method1';
}
}
class plugin2 extends plugin1 {
public function coremethod2(){
echo 'plugin2method2';
}
}
$core = core::create();
// test what we have
echo get_class( $core ), '<br>'
, $core->coremethod1(), '<br>'
, $core->coremethod2()
;
If your only concern is that not including plugin1 will create an error, then you can resort to autoloading to have plugin2 load plugin1 automatically:
From the comments in the PHP Manual on spl_autoload
// Your custom class dir
define('CLASS_DIR', 'class/')
// Add your class dir to include path
set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR);
// You can use this trick to make autoloader look
// for commonly used "My.class.php" type filenames
spl_autoload_extensions('.class.php');
// Use default autoload implementation
spl_autoload_register();
If, however, you are looking for a traits/mixin-like feature, then the answer is no. PHP does not support this as of now. At least not without patching the core or resorting to these two APIs you do not want to use in production code.
The proper way to change how an object behaves at runtime would be to use Decorators:
$class = new BasicCache( new BasicValidators ( new Basic ) );
or Strategy patterns:
$class = new Basic;
$class->setStrategy(function() { return 'foo'} );
echo $class->callStrategy(); // foo
$class->setStrategy(function() { return 'bar'} );
echo $class->callStrategy(); // bar
See http://sourcemaking.com/design_patterns for the most common patterns.
EDIT Here is an example of how to create plugins with decorators. Assume, we have a game of some sort where some non-player characters walk around in a virtual space and greet the main character from time to time. That's all they do right now. We want some variation on how they greet though, which is why we need our plugins/decorators in this scenario.
First we create an interface that defines some methods any object able to greet should have. We don't care about what it does when these methods are invoked on a specific object. We just want to make sure that the methods are available and that they are called with a clearly defined input:
interface GreetInterface
{
public function greet($name);
public function setGreeting($greeting);
}
An interface is basically a contract any implementing object must fulfill. In our case, the contract says, if you are an object that can greet, you have to have two methods. Implement them any way you like, but have these methods.
Let's build our non-player character classes now, implementing this interface
class Dude implements GreetInterface
{
protected $greeting = 'hello';
public function greet($name)
{
return sprintf('%s %s', $this->greeting, $name);
}
public function setGreeting($greeting)
{
$this->greeting = $greeting;
return $this;
}
}
That's pretty straigtforward I guess. The Dude class just defines the two methods from the interface. When greet() is called, it will fetch the string stored in greeting and prepend to the param passed to the greet method. The setGreeting method allows us to change the greeting at runtime. Note: you could add a getter as well (I was just lazy)
Now on to the plugins. We will create an abstract GreetPlugin class to contain some shared boilerplate code, simply because we don't want to duplicate code in our actual plugins. The abstract plugin class will implement the GreetInterface, so we can make sure all subclasses implement the interface too.
Since Dude already implements the interface as well, we could have the plugins extend Dude, but that would be conceptually wrong, because extending creates an is-a relationship, but a plugin is not a Dude.
abstract class GreetPluginAbstract implements GreetInterface
{
protected $inner;
public function __construct(GreetInterface $inner)
{
$this->inner = $inner;
}
public function setGreeting($greeting)
{
$this->inner->setGreeting($greeting);
return $this;
}
public function greet($name)
{
return $this->inner->greet($name);
}
}
The plugin class accepts one argument when initialized: any class implementing the GreetInterface. The TypeHint makes sure, the class fulfills the contract. That's required, because, as you can see in the code, our plugins will need to call the methods in the interface on the class passed through the constructor. If we had extended from Dude, we would now be able to wrap dudes into dudes, which is a bit odd. Another reason for not doing it.
Now on to the first plugin. We want some of our dudes to speak with a fancy french accent, which means they use âccénts all over the place, but cannot pronounce a proper h. Disclaimer: yes, I know that's a cliche. Please bear with my examples
class FrenchPlugin extends GreetPluginAbstract
{
public function greet($name) {
return str_replace(array('h', 'e'), array('', 'é'),
$this->inner->greet($name));
}
}
Since the Plugin extends the abstract plugin, we can now focus on the actual code that modifies how a regular dude would do his greeting. When greet() is called, we call greet() on the wrapped element and then remove all h characters and turn all es into és. Everything else is unmodified abstract behavior.
In another plugin, we want to change the wording of the greeting, so we have some dudes say Heya, instead of just Hello. Just to add some variation.
class EasyGoingPlugin extends GreetPluginAbstract
{
protected $inner;
public function __construct(GreetInterface $inner) {
$this->inner = $inner->setGreeting('heya');
parent::__construct($inner);
}
}
This way we only override the constructor, because the greet method should just return whatever it will be. So we call the setGreeting method on the object passed to this plugin. Because the object has to implement the GreetInterface, we can be sure this works.
Note that I am assigning the return value of setGreeting as the inner object. This is possible because I return $this, whenever setMethod is called. This cannot be enforced through the interface, so you cannot rely on this form the interface. I just added it to show another technique: method chaining.
With two plugins done, we feel we have enough variation. Now we only need a convenient way to create Dudes. For that we create a small class like this:
class DudeBuilder
{
public static function build()
{
$dude = new Dude();
$decorators = func_get_args();
foreach($decorators as $decorator) {
$decorator .= "Plugin";
// require_once $decorator;
$dude = new $decorator($dude);
}
return $dude;
}
}
Note: I always mix up Builder and AbstractFactory, so if the above is a Factory, well, then it's a factory. Check out the design patterns links I gave earlier on ;)
All this Builder does, is create a regular dude and then wrap/decorate it into/with whatever plugins we tell it to use and than return it. Because the builder encapsulates no own state, we make the build method static.
For this example I assume you used the autoloading code I gave right on top. If not, you can include the plugin files in the foreach loop. Lazy loading them only when they are needed will give you a few microseconds faster load times over including them all on top. Hopefully, this also explains what I meant in the various comments when I argued the behavior should not be controlled by a file inclusion. The file inclusion is just a necessity. You cannot use a class that is not know to PHP. But that the class is actually used, is controlled by our code alone, by passing in the plugin names to the build method.
Let's do this now
$regularDude = DudeBuilder::build();
$frenchDude = DudeBuilder::build('French');
$easygoingDude = DudeBuilder::build('EasyGoing');
$frenchEasyGoingDude = DudeBuilder::build('French', 'EasyGoing');
This is effectively the same as doing:
$regularDude = new Dude;
$frenchDude = new FrenchPlugin(new Dude);
$easygoingDude = new EasyGoingPlugin(new Dude);
$frenchEasyGoingDude = new FrenchPlugin(new EasyGoingPlugin(new Dude));
With just two plugins, we can now create three types of Dudes. Let's have them greet you:
echo $regularDude->greet('Yuri'), PHP_EOL,
$frenchDude->greet('Yuri'), PHP_EOL,
$easygoingDude->greet('Yuri'), PHP_EOL,
$frenchEasyGoingDude->greet('Yuri'), PHP_EOL;
// gives
hello Yuri
éllo Yuri
heya Yuri
éya Yuri
We can now create additional plugins to decorate our basic classes with. If for some reason, you decide your game should have talking horses or cars as well, you could also create a class Car or Horse and have it implement the greet interface too and add a Builder for them. You can then reuse the plugins to create French EasyGoing Cars or Horses.
PHP core can be extended with PECL extensions (which are C++, I believe).
Core functions can be overridden (if you have the APD PECL extension installed) with override_function
User functions can be executed with call_user_func.
Maybe if you could explain what you are planning, we'd be able to offer a better answer?
Your code is breaking because plugin2 extends plugin1, and you're not including the plugin1 class. Why not make class plugin2 extend core? That seems to be what you're going for.

Categories