PHP Class modeling issue - php

I've written a few classes and have come to a fork in the road about what I should do. My base question is, how do I avoid duplicating my code across classes with very similar functionality? Traits are not an option for me right now, and I don't think they would help too much here anyway.
I have the following classes implemented.
//either a directory or a file on the file system
class FileSystem_Object{
//the size of the class in bytes
public function getSize(){}
//same as phps native realpath
public function getRealPath(){}
}
//a zip file on the file system, e.g. files that end in .zip extension.
class FileSystem_Object_Zip extends FileSystem_Object{
//returns size of all files if they were to be uncompressed, in bytes
public function getUncompressedSize(){}
}
//a singleton file that keeps track of only one object copy of a file
class FileSystem_Manager{}
These classes sortof provide me with some SPLFileObject type functionality. I can do the following kind of stuff
$object =
FileSystem_Manager::getInstance()
->getFileSystemObjecT( '/some/path/to/file/or/directory/' );
Every time I call the getFileSystemObject method it will either return a new class object or return an object that was assigned to that path already, thus avoiding me creating multiple objects that point to the same path on the filesystem. ( maybe not the best idea but that's what I went with. )
Here's where we get to the issue a bit.
I have another set of classes that I use to 'lock' objects. Right now the only objects that I'm locking are filesystem_objects, regardless of whether they're directories or files. This works simply enough by creating a lock file for the file based on the process id of the php process trying to lock it.
inteface Lockable_Object{
public functon getLockableIdentifier();
}
class Lockable_FileSystemObject implements Lockable_Object{
/**
* I return a false here if the file doesn't exist
* so any other processes trying to lock this file will
* know they can no longer lock it because it has moved
* or been renamed for some reason.
*/
public functon getLockableIdentifier(){
if( file_exists( $this->_fullFilePath ) ){
return $this->getRealPath();
}
return false;
}
}
The problem I face now is that I'd like to create a Zip file object that can be locked as well, and I'd like to be able to lock pretty much any file/directory but I DON'T want to have to duplicate code. Which of the following should I do
//Option 1
class Lockable_Object_Zip extends FileSystem_Object_Zip
implements Lockable_Object{
//In here I would have to duplicate the getLockableIdentifier method and do that
//for every file type i decide to make in the future
}
//Option 2
class Lockable_Object_Zip extends Lockable_FileSystemObject
//In here I would have to duplicate all the zip functionality coded into
//FileSystem_Object_Zip
}
//Option 3
class FileSystem_Object implements Lockable_Object{
//build in the 'lockablity' into the base class
}
Right now I'm leaning towards option 3 but the only reason I would not like to do that is because then I would HAVE to have the 'Locker' part of my library whenever I want to use the file system stuff; it would be more tightly coupled.
I'm sure you'll have comments about the design and some will say "SplFileObject does all/most of this". I've included methods in here for examples and not all the methods I implemented are here so this isn't the only functionality I've written. All these comments and more are welcome, however, because they might land me on a design that will avoid this whole issue.
Thank you

In case the type of the locked classes doesn't matter, you could go with a Decorator pattern, e.g.
class Lockable
{
protected $lockable;
public function __construct($lockable)
{
$this->lockable = $lockable;
}
public function lock()
{
// .. your code to lock $this->lockable
}
public function __call($method, $args)
{
return call_user_func_array(array($this->lockable, $method), $args);
}
}
This way you are not duplicating logic. The drawback is that you cannot use the decorated instance in methods requiring the decorated type (unless you add appropriate interfaces and delegate all calls).
The Strategy pattern would be another option:
class LockingStrategy
{
public function lock($fileSystemObject)
{
// your code to lock $fileSystemObject
}
}
class ZipFile
…
public function __construct(LockingStrategy $strategy)
{
$this->lockingStrategy = $strategy;
}
public function lock()
{
$this->lockingStrategy->lock($this);
}
}

I think you should look into the Strategy pattern. Consider using composition of a Lockability strategy rather than trying to inherit it.

Related

PHPUnit: How to test a method which calls another function declared in different file

I'm trying to test a method using PHPUnit, where it calls another function (standalone function, without class), which resides in different file which does a some pretty good calculation and returns a object.
This is my actual main code:
class CreateRecords
{
public function createEntities($details)
{
if (trim($details['username']) == "") {
$this->result = "Username is empty.";
} else {
$this->result = create_record($Details['username']);
}
return $this->result;
}
}
This create_record function, (standalone function, without class), which is core function, resides in separate file and it does pretty good calculations (calls lots of other methods/functions) and returns object, whether it is successful or not.
I can mock the createEntities method, but I want to mock the create_record function, which does all the computations and returns the result.
I have seen few posts which has a somewhat similar scenario,
phpunit testing method that calls other class methods which need mock
PHPUnit mock method used in another class
But I am unable to understand, how to mock standalone function which is declared in some different file.
You can create new method that will be returning result from outside function.
Then you can mock this new method
class CreateRecords
{
public function createEntities($details)
{
if (trim($details['username']) == "") {
$this->result = "Username is empty.";
} else {
$this->result = $this->createRecord($Details['username']);
}
return $this->result;
}
public function createRecord($username){
return create_record($username);
}
}
Namespaces in PHP5.3+ offer an excellent solution to this, which allows you to override built-in functions in your current namespace.
Longer term, refactoring the global create_record() into a class which can be injected into your CreateRecords class, and then mocked, would be a good way to go. In this instance, the global function would just be a simple wrapper to call the class until the entire application was updated.
To re-create the create_record($username) is not hard though, and useful for tests. The same technique can be used to override the global time() function as well.
Within the test-file, add a new (but local) namespace:
<?php
namespace Test\DatabaseAccess;
use DatabaseAccess\CreateRecord;
use PHPUnit\Framework\TestCase;
namespace DatabaseAccess {
function create_record($username)
{
// pretend to do something
}
}
class CreateRecordTest extends TestCase
{
// test to check CreateRecord class
// which should call the `create_record`, above
}
This is the same technique that the SymfonyBridge system uses to create ClockMock - which dynamically adds time(), sleep(), etc to the namespace that you are unit-testing (in this example, the namespace DatabaseAccess\CreateRecord, not the Test\ prefixed namespace). ClockMock (and DnsMock) does it with an eval() call, but since you know the namespace explicitly, you can write it into the test file itself for clarity.
After reading the above (pretty good) answers and your comments saying that you cannot touch the tested class - CreateRecords,
I can suggest another solution that is not ideal but should get the job done:
Create a new class that inherits\ extends from CreateRecords - CreateRecordsExtended.
The extended class should override only the tested function in question createEntities($details). so create a new one and copy the code from the original function.
Also, create a new function create_record().
Now, inside the new createEntitied, call your version of create_record - $this->create_record(), instead of calling the global function.
Now you can mock it! and even because this class is used only for testing, you can even just retun whatever you want from it, and do not even have to mock it now.
This class can reside either in the regular code folder, or as a neighbor of your testing class- as it is used only for testing.
Pros:
existing code is not modified at all.
you still use same exact
functionality, besides the mocked function, which is what you wanted.
Cons:
you will be testing a different class then the one you wanted to, but
it still close enough.
code inside function createEntities needs to
be copied
Perhaps its not most ideal, but should get the job done. hope i helped.

optional dependencies within a class

I'm looking for some direction regarding the following, I'm new to OOP and getting there but think either my lack of understanding is causing me to get stuck in a rabbit hole or I'm just over thinking things too much and being anal.
basically i have a main class called "CurlRequest" which sole purpose is to perform curl requests, providing a url and params it returns me some html. This class works and functions as intended and I'm happy with that.
I use this class for a few projects but for one I then wanted to track the performance of my requests made. attempted, failed, passed etc, so i created a static class for this which manages all my counters. I place counter references like the following at different areas in my CurlRequest class.
PerformanceTracker::Increment('CurlRequest.Attempted');
PerformanceTracker::Increment('CurlRequest.Passed');
PerformanceTracker::Increment('CurlRequest.Failed');
I have around 10 or so of these with my class tracking all kinds of things during the curl request and i also use my PerformanceTracker class in other classes i made.
However like mentioned i only wanted to do this for one of my projects, so find my self in the situation of having my original CurlRequest class and an altered one with performance counters in it.
My question is, is their a way i can use the same class for any project and choose to use the PerformanceTracker class or not. The obvious way i thought of was to pass an $option argument into the class and then have if statements around all the counters, but can't help think its messy.
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($this->owner . '.CurlRequest.Failed');
}
this also adds a lot of extra code to the class.
I suggest placing the if statement in a separate method
private function handlePerformanceTracker($q)
{
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($q);
}
}
And call this method instead of your calls to
PerformanceTracker::Increment(...);
Also if you find that you want to track performance differently between your projects it might be useful to change your constructor to accept a callable argument, this way you externalize the actual implementation from the CurlRequest class itself.
public function __construct(..., callable performanceHandler)
Then when you instantiate your class:
$curlRequest = new CurlRequest(..., function($outcome) {
//your implementation
});
You can use inheritance and create a subclass that performs the logging before delegating to the parents methods:
class PerformanceTracker
{
static function Increment($s)
{
echo $s;
}
}
class CurlRequest
{
function get($url){
//preform curl request, save html to variable etc
//dummy vars used here so working example code
$html = 'html here';
$curlError = false;
if($curlError){
$this->error($curlError);
}
return $this->success($html);
}
protected function success($html)
{
return $html;
}
protected function error($curlError)
{
throw new Exception($curlError);
}
}
class LoggingCurlRequest extends CurlRequest
{
function get($url)
{
PerformanceTracker::Increment('CurlRequest.Attempted');
return parent::get($url);
}
function success($html)
{
PerformanceTracker::Increment('CurlRequest.Passed');
return parent::success($html);
}
function error($curlError)
{
PerformanceTracker::Increment('CurlRequest.Failed');
parent::error($curlError);
}
}
$lcr = new LoggingCurlRequest();
$lcr->get('unused in example');
As i have used dummy classes with minimal code to demo the technique the benefit might not be obvious, but in you real code, the methods in the CurlRequest class will be more complex, but the methods in the logging class will remain as two liners, with the log function and the call to the parent method.
Using this technique you can modify the parent class without effecting the derived classes (provided the method signatures dont change), can create other derived classes (how about a CachingCurlRequest) etc.
For the full benefits of OOP you should look into dependency injection and interfaces
From an OOP perspective you could use the 'Null' object pattern. This just means that the dependency used by the CurlRequest class is abstract (possibly an interface?). You would then have Two concrete implementations of PerformanceTracker: the one you have today and one that does nothing (it does not have any behavior). In this way for the one project when you instantiate the CurlRequest class it would use the concrete implementation that has behavior and for all the other projects it would use the concrete implementation with no behavior. All of the code in CurlRequest would look the same but it would have different behavior depending on which concrete implementation it was using

OOP & PHP Runtime

I'm new to OOP & PHP. I've followed the OOP Approach as best I can so for each of the prominent "nouns" in my specification I have a class and this is working well.
But it got me thinking about compilation and runtime.
If I develop a comprehensive class, that has relevent properties, setters and getters and a series of methods, the resulting class could easily run into a couple of hundred lines of code.
If I then use this class, whereby (for example) I only call one method hasn't the whole class got to be parsed & converted and memory allocated before execution?
Have I got this right? As surely a procedural approach would be far more efficient (a function within an include) or is it that in real-world runtime this is negligable?
In regard to
But it got me thinking about compilation and runtime.
I think #Mark comment is most pertinent:
most class based systems use an autoloader, which only includes classes/files that are actively required to handle the request.....
Atomic classes with single responsibilities, autoloaded from a dependency injection container would produce a low amount of unused code.
If I then use this class, whereby (for example) I only call one method hasn't the whole class got to be parsed & converted and memory allocated before execution?
Lengthy classes can sometimes be broken into smaller classes, which limits the amount of code loaded to execute a single method. I think the key here is the single responsibility principle, inducing classes that will need the bare minimal resources they need to operate the function they are called to do.
Due to the fact that all included files are parsed, and a few hundred lines of code is not that much, but if you want you can simply think about some static helper methods for this kind of functions if it's possible.
In combination with an autoloader you could really drop mass includes.
Example before
One class,that you always include:
class Helper {
public static function createFolder() {
// some stuff
}
public static function myEncoder() {
// some stuff
}
public static function getDomElementByID() {
// some stuff
}
public static function cleanupString() {
// some stuff
}
}
Example afterwards:
Split your "huge" class and try to group the methods in it.
class StringHelper
class StringHelper {
public static function cleanupString() {
// some stuff
}
}
class FilesystemHelper
class FilesystemHelper {
public static function createFolder() {
// some stuff
}
}
class DomHelper
class DomHelper {
public static function getDomElementByID() {
// some stuff
}
}
class EncodingHelper
class EncodingHelper {
public static function myEncoder() {
// some stuff
}
}
Autoloader
Create a autoloader class, more information here => PHP Docs
The Autoloader will only include the files when you actually need it. For example if you are calling:
$stringHelper = new StringHelper();
// or
StringHelper::someStaticFunction();
What really makes a blast to work with is the combination of namespaces and autoloader. take a look here => PHP Docs (namespaces)

How to avoid this addition to our god object

Our web application allows users to upload files. We have two god objects in our web application:
User object (3000+ lines)
File object (3000+ lines)
Common usage at the moment is:
$file = File::getByID($_GET['file_id']);
$user = User::getByID($file->ownerID);
echo $file->title;
echo $user->name;
We are currently discussing adding the user object to the file object as another public variable. Because everywhere we use the file object, we need the associated user object.
So usage will be:
$file = File::getByID($_GET['file_id']);
echo $file->title;
echo $file->user->name;
We are trying to refactor these god objects, and adding the user object to the file object feels like we are making the problem worse. However I don't really have a good argument to disagree with the proposed change.
It would be great to get some help with the following questions:
Is adding the user object to the file object a bad idea?
If it's a bad idea, what is the main argument for not doing so?
How else can I go about refactoring these objects?
Notes
I know number of lines aren't a true measure, but our other classes are 50-150 lines and rarely used.
I've cut down the common usage to the important bits so apologies for lack of best practices.
I'm already trying to remove static methods and public variables from our code.
I would definitely add the User object as a property of the File class, since this is the actual representation of the data model behind it.
It will also give the code more clarity, and will produce more concise usage code of the File object.
This will also mean the File class does not have to do any checking for validity if an owner ID is set: if an object is passed, it can assume it is valid (or it should not have been able to be constructed).
class File
{
protected $owner;
/**
* #var User $owner mandatory
*/
public function __construct(User $owner)
{
$this->setOwner($owner);
}
public function setOwner(User $owner)
{
return $this->owner;
}
public function getOwner()
{
return $this->owner;
}
}
It is now clear that the File has a mandatory property owner, which is a User.
I would also recommend using accessors instead of public properties. This will be more future proof, for example it will also allow you to lazy load the user if you please to do so later on.
class File
{
protected $ownerId;
protected $owner;
public function getOwner()
{
if (!$this->owner && $this->ownerId) {
$this->owner = User::getById($this->ownerId);
}
return $this->owner;
}
}
This implies that your data layer fills the object accordingly, and would also require an adapted constructor, with the loss of automatic Type checking (only the constructor, still has type check in the setter):
/**
* #var $owner int|User the user object or it's ID
*/
public function __construct($owner)
{
if (is_object($owner)) $this->setOwner($owner);
else $this->ownerId = $owner;
}
As for the question of where your static User::getById() should go: you should separate the data operations (retrieval and persisting of objects) in a separate layer.
class UserStore
{
public function getById($id)
{
// code to retrieve data from where ever, probably database
// or maybe check if the user was already instantiated and registered somewhere...
$user = new User($data['name'], $data['email']/*, ....*/);
return $user;
}
}
For this last one, I would really not recommend building your own library (called an ORM), many great implementations exist. Take a look at Doctrine ORM.
Lastly I would say that reducing the size of a class should not be a goal in itself. you could however reduce it's complexity by extracting logic that is not inherent to the object itself, as is the case with the owner property.
Another example might be the File's path: maybe the path is stored relatively, and you have functions to convert that to an absolute path, or to get the extension or filename. This functionality can be extracted in a seperate FileMeta class or whatever you want to call is, maybe even a File class in a different namespace: FileSystem\File.

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