Ascertaining controller and action from my own class in CakePHP 2.4 - php

I have created a class of my own which I'm using from methods which override those in a couple of Cake's caching-related classes in order to customise the filenames of the files that CacheHelper saves. (The filenames sometimes need to include the type of user so that logged in users don't see cached versions of what non-logged in users would see and vice-versa, for example.)
My class is called MyCacheUtility and resides in app/Lib/Cache.
In this class, I need to ascertain what the controller and action are. What is the best way of doing so, and can I use Cake's CakeRequest class?
So far, I have included App::uses('CakeRequest', 'Network');, and...
I can't use $this->params('controller') as my class isn't a controller or component:
Error: Using $this when not in object context
File: /srv/www/app/Lib/Cache/MyCacheUtility.php
Line: 20
I can't use CakeRequest::param('controller') as (if I understand correctly) CakeRequest's methods are not designed to be used statically:
Error: Using $this when not in object context
File: /srv/www/cakephp/lib/Cake/Network/CakeRequest.php
Line: 864
The contents of my class:
App::uses('CakeRequest', 'Network');
class MyCacheUtility {
/**
* Sets the cache file’s filename to include references to user type
* and organisation (depending on the current controller and action)
* so that both logged-in users from different organisations and visitors
* get the right stuffs.
*/
public static function getPrefix() {
$prefix = '';
switch ('controller') . '/' . 'action') { // this is where I need the controller and action
case 'user_lists/organisation_lists':
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.organisationName');
break;
}
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.type');
return $prefix;
}
/**
*
*/
private static function sessionDataToFilename($sessionValue) {
if (CakeSession::read($sessionValue)) {
return strtolower(Inflector::slug(CakeSession::read($sessionValue))) . '-';
}
}
}
Update:
I found I wasn't able to pass the CakeRequest object from CacheDispatcher to MyCacheUtility class, and I think this is because CakeRequest has not been instantiated at the point at which CacheDispatcher does its stuff?
I do now have what I set out to do working. I'm passing Cake's $path variable through to my method and manually doing some processing based on this:
class MyCacheUtility {
public static function getPrefix($path) {
$pathParts = explode('/', parse_url($path, PHP_URL_PATH));
$prefix = '';
if (count($pathParts) > 1) {
if ($pathParts[1] == 'shared_lists') {
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.organisationName');
}
}
$prefix .= MyCacheUtility::sessionDataToFilename('currentUser.type');
return $prefix;
}
private static function sessionDataToFilename($sessionValue) {
if (CakeSession::read($sessionValue)) {
return strtolower(Inflector::slug(CakeSession::read($sessionValue))) . '-';
}
}
}
I was hoping to be able to directly apply Cake's own logic, but this seems to be doing the job okay so far.

Why do you need a library class at all? Extend the CacheHelper and use your customized cache helper via aliasing as $this->Cache in the views. To modify the file names it should be enough to override CacheHelper::_writeFile().
If you want to modify the caching itself write a new cache engine or extend an existing one to modify it's behavior. Your question does not really contain much information about why and what exactly you want to do so I can't give a better advice.
The CakeRequest object should be nearly everywhere present, so just pass it to the constructor of your cache lib. But like I said, architecturewise I doubt your approach is the best path to go. Show your cache lib code?
new MyCacheLib(CakeRequest $request);
Edit:
Write your custom Cache dispatcher or extend the existing one instead. You'll get the request object passed to it by default. Take a look at the CacheDispatcher filter. No need for a lib class here.
A more simple approach might be to write a custom cache engine and configure the helper to use that, same for the cache dispatcher filter. This way you can share the code. Or use a trait (php 5.4) to share the path building logic between the filter and the helper.

Related

CakePHP $this Syntax

I've been trying to learn CakePhp for a while now but I still can't get alot of stuff. I've been reading a lot and watching videos. I just want to ask a very simple question.
I've been trying to mess with the bookmarks tutorial and i'm watching a video. In the video he baked a component called Validate. In the cmd he typed
bin/cake/bake component Validate
Then a ValidateComponent.php appeared in the component folder in the controller. Now he used the ValidateComponent.php by going to the BookmarksController and adding to the initialize method
$this->loadComponent('Validate');
I just want to ask where did the word validate come from? shouldn't it be ValidateComponent? and where does he get the loadComponent from? I've seen him using $this->method(); or $this->method('string', [array]); I just want to know how the syntax works and what each word means. Sorry for the long explanation. I'm really trying to learn and i'm really confused. thank you very much.
ValidateComponent.php
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
/**
* Validate component
*/
class ValidateComponent extends Component
{
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
public function validLimit($limit, $ default)
{
if (is_numeric($limit)){
return $limit;
}
return $default;
}
}
part of BookmarksController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Validate');
}
I can't seem to find where he got the word 'Validate'
Every controller in your application extends a base Controller Controller or AppController which extends Controller
Controller have bunch of methods, One of these methods is the loadComponent() (See Source)
public function loadComponent($name, array $config = [])
{
list(, $prop) = pluginSplit($name);
$this->{$prop} = $this->components()->load($name, $config);
}
Why Validate instead of ValidateComponent?
Short answer: suffix.
See predefined suffix in App class
CakePHP uses suffix to load classes, When you hit loadComponent() You go to ComponentRegistery class to Register the component, ComponentRegistery will call App class to load class. __loadClass()
Almost everything in CakePHP has a suffix, In your case ValidateComponent has the Component suffix.
return App::className($class, 'Controller/Component', 'Component'); (Source)
I hope this will make more sense to you
$this isn't specifically anything to-do with cake but part of PHP itself. In object oriented context $this refers simply to the current class.
$this->something refers to an object within the current scope. This could be within the current class or from an extends or use.
$this->something(); refers similarly to a method or function within the current scope.
If are using an IDE such as netbeans you can usually click through these references to see the object they refer to so for example if you do in fact use Netbeans you could ctrl-click on $this->loadComponent('Validate'); to see the actual function it refers to.
Regarding where does 'Validate' come from, it's a string you are passing to that object. On the other end it will be used in the function, probably in a switch or if statement to return something.
Eg:
Public function loadComponent($type){
If($type == 'Validation'){
//do something
}
}

PHP late static bound referencing

Situation
In this web app I am building there is a "bootstrap" sequence that defines (through constants) and initiates an extended controller. Currently, the controller keeps track of assets (script files, css, etc.) that will be deployed at the later render stage through a series of static variables. I will simplify the code here, think of it as pseudo-PHP.
/* CONTROLLER CLASS */
class Controller {
protected static $aryScriptFiles = array();
public function __construct() {
/* Behaviour */
/* Some logic that identifies/calls Home_Controller method Index */
}
public static function Add_Script($strFileName) {
static::$aryScriptFiles[] = $strFileName;
}
}
/* HOME_CONTROLLER CLASS */
class Home_Controller extends Controller {
protected static $aryScriptFiles = array('default', 'carousel', 'etc');
protected function Index() {
/* Behaviour */
/* Load the view as an include. It is "part" of the User_Controller */
}
}
/* EXAMPLE_HELPER */
class Example_Helper {
public static function Test() {
/* THE NEXT LINE IS IMPORTANT FOR THE QUESTION */
$objController = CONTROLLER;
$objController::Add_Script('dominoes');
}
}
/* INDEX VIEW FILE */
<h1>Welcome!</h1>
<?php
echo get_class(); <-- Would echo 'User_Controller'
Example_Helper::Test();
/* Simplification of render process */
foreach(static::$aryScriptFiles as $strFileName) {
/* Render the HTML script tag */
}
?>
Flow
Ok, given the above there is a bootstrap that ends up calling User_Controller. For examples sake, I have simply defined them to let you know what state the script will follow.
$strControllerName = 'User_Controller';
define('CONTROLLER', $strControllerName);
$objController = new $strControllerName();
What you end up with is the aryScriptFiles array having 4 entries and this works great.
Problem
Before reading on, please note I do not want to use magic methods, globals or have to pass a reference of the controller name to the Helper function.
I would like to try and remove the line in the helper file that pulls the current controller name to a variable from the constant.
$objController = CONTROLLER; <-- I want this to shoo shoo
If I were to just try and use the following, the script file that gets added by aid of the Helper is part of the original Controller array as opposed to the Home controller.
Controller::Add_Script('dominoes'); <-- Will not be part of the Home_Controller array
Question
Please can I have some opinions from the SO community on what you feel the best approach to tackle this would be taking in to account that the controller name will differ? My primary objectives in this exercise are:
Keep the View file VERY simple
Keep the Helper files simple.
Avoid the need to add any code more than necessary to the Home_Controller
I'm currently thinking that one of the best options would be to host the "assets" within a seperate class, just want to know whether it is possible.
Thanks for reading.
First of all, think about your seperation of concerns. Should it really be the responsibility of a controller to manage assets?. Why did you made the method for adding assets static in the first place?
I do not want to use magic methods, globals or have to pass a reference of the controller name to the Helper function.
What are you expecting? If you try to force a class to depend on another class in a completely different scope and context your only option is to use ugly hacks to make your object globally accessible.
Dependency Injection to the rescue
Why should your helper know about what controller and how the controller is treated from the outside?
The only thing your helper should do is to operate with the controller (in your case). It should not try to magically detect what controller is being used. It should just take a controller and operate with it.
class Example_Helper {
public static function Test($controller) {
$controller::Add_Script('dominoes');
}
}
Example_Helper::Test($objController);
Since the addScript() method and the $aryScriptFiles property is static anyways, you could also just call the method in the helper on the parent controller. It would make no difference.
Also why do you want to talk to your controller from the view? The view should be "dumb" it should not be able to hold and operate with data except those that were passed to it by the controller.
Wouldn't it make more sense to add functionality to your controller or one of it's services that passes the required assets to your view, instead of forcing the view to get it's data from from the controller by itself?
I think there are a few logical flaws in your code here. Especially your usage of static properties and methods. If you could clarify that a bit I could go in detail a bit.
Apart from architectural concerns (assets should indeed be managed by a separate AssetManager) your problem can be relatively easily solved because of PHP's rather peculiar own architecture, specifically exposed through methods like get_called_class. This allows you to write code like this:
$assets = []; // Global for brevity of example
class Base {
static function addScript($script)
{
global $assets;
$myName = get_called_class();
$assets[$myName][] = $script;
}
}
class Derived extends Base {
public function __construct()
{
self::addScript('test');
}
}
$foo = new Derived();
var_dump($assets);
Which will then output the following:
array(1) {
["Derived"]=>
array(1) {
[0]=>
string(4) "test"
}
}
Note that using get_class instead of get_called_class would here show the array's name as Base instead of Derived, while Derived is what you need. This way you can embed helper functions in Controller, which automatically derive the class name and forward it to the central asset manager.

Seamlessly using a template system with Kohana?

I'm planning to use Mustache templates along with Kohana in my next project. So what I'm trying to do is to make Kohana seamlessly use Mustache whenever rendering a view. For example, I would have this file in my views folder:
myview.mustache
Then I can do in my application:
$view = View::factory('myview');
echo $view->render();
Just like I would do with a regular view. Does Kohana allow this kind of thing? If not, is there any way I could implement it myself using a module? (If so, what would be the best approach?)
PS: I had a look at Kostache but it uses a custom syntax, which for me is the same as using Mustache PHP directly. I'm looking to do it using Kohana's syntax.
Edit:
For information, this is how I ended up doing it, based on #erisco's answer.
The full module is now available on GitHub: Kohana-Mustache
In APPPATH/classes/view.php:
<?php defined('SYSPATH') or die('No direct script access.');
class View extends Kohana_View {
public function set_filename($file) {
$mustacheFile = Kohana::find_file('views', $file, 'mustache');
// If there's no mustache file by that name, do the default:
if ($mustacheFile === false) return Kohana_View::set_filename($file);
$this->_file = $mustacheFile;
return $this;
}
protected static function capture($kohana_view_filename, array $kohana_view_data) {
$extension = pathinfo($kohana_view_filename, PATHINFO_EXTENSION);
// If it's not a mustache file, do the default:
if ($extension != 'mustache') return Kohana_View::capture($kohana_view_filename, $kohana_view_data);
$m = new Mustache;
$fileContent = file_get_contents($kohana_view_filename);
return $m->render($fileContent, Arr::merge(View::$_global_data, $kohana_view_data));
}
}
Yes, you can. Since Kohana does some trickery with autoloading, what they dubbed "Cascading Filesystem", you can effectively redefine the functionality of core classes. This is something that Code Igniter also does, if you are familiar.
Particularly, this is the View::factory method you are referring to. Source.
public static function factory($file = NULL, array $data = NULL)
{
return new View($file, $data);
}
As you can see, this returns an instance of View. Initially, View is not defined, so PHP goes looking for it using autoloading. This is when you can take advantage of the cascading filesystem feature by defining your own View class, which must be in the file APPPATH/View.php where APPPATH is a constant defined in index.php. The specific rules are defined here.
So, since we can define our own View class, we are good to go. Specifically, we need to override View::capture, which is called by $view->render() to capture the inclusion of the template.
Take a look at the default implementation to get an idea of what to do and what is available. I've outlined the general idea.
class View
{
/**
* Captures the output that is generated when a view is included.
* The view data will be extracted to make local variables. This method
* is static to prevent object scope resolution.
*
* $output = View::capture($file, $data);
*
* #param string filename
* #param array variables
* #return string
*/
protected static function capture($kohana_view_filename, array $kohana_view_data)
{
// there
$basename = $kohana_view_filename;
// assuming this is a mustache file, construct the full file path
$mustachePath = $some_prefix . $basename . ".mustache";
if (is_file($mustachePath))
{
// the template is a mustache template, so use whatever our custom
// rendering technique is
}
else
{
// it is some other template, use the default
parent::capture($basename, $kohana_view_data);
}
}
}

Do _initX() functions get called in sequence

In my bootstrap.php I have many _initX() functions, and some of them may contain code that depends on code in the previous initX
protected function _initAutoloading() { }
protected function _initViewInitializer() { }
protected function _initVariables() { }
So my question, are these _init functions guaranteed to be executed in the exact order they've been declared?
EDIT - To provide a more direct answer to your question, I would say that they probably will be since the code uses ReflectionObjects::getmethods() or get_class_methods depending on your PHP version, so I believe those will return the function in order but there is nothing in the PHP docs or Zend docs that guarantee this will always be the case, so I would not consider this a supported feature.
You can pass the names of the resource functions you want/need to call as part of the bootstrap call: $bootstrap->bootstrap(array('foo', 'bar')); instead of not passing anything and let the Zend Application call them all automatically in which you are not sure of the order.
If you have dependencies in between your bootstrap resources however, I suggest you look at Resource plugins which will allow you to separate your code in different classes and easily call $bootstrap('foo') from within your 'bar' resource plugin code (though you can do so with the _init*() functions as well)
Another benefit of resource plugins is they can be shared with other bootstrap files if you need to and they are easier to test than _init*() functions.
Make sure you read theory of operation document from the Zend Application doc
If you really need them invoked in a particular order, you should use a helper list:
var $init_us = array(
"_initAutoloading",
"_initViewInitializer",
"_initVariables",
);
function __construct() {
foreach ($this->init_us as $fn) {
$this->{$fn}();
}
}
To use that construct in ZF you could rename the example __construct into _initOrderedList and your custom _initFunctions into _myinit... or something.
Read the manual. There are a section called Dependency Tracking :
If a resource depends on another resource, it should call bootstrap() within its code to ensure that resource has been executed. Subsequent calls to it will then be ignored.
Here is sample code :
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequest()
{
// Ensure the front controller is initialized
$this->bootstrap('FrontController');
// Retrieve the front controller from the bootstrap registry
$front = $this->getResource('FrontController');
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);
// Ensure the request is stored in the bootstrap registry
return $request;
}
}
You don't have to rely on the order.

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