Operating on all files/classes in a directory - php

I am just starting with PHPunit so not sure if I'm missing something basic but I'd like to have a test case:
scan a particular source directory,
find all files with .PHP extension,
then attempt to instantiate each of the classes represented by these files
test that each class returns the right properties from particular call (all classes in this directory are subtypes of a common base class)
Make sense? The setup code would look like this:
protected function setUp() {
$this->documentFiles = glob('*.php');
$this->documentClasses = array_map(function($file) {
return substr($file,0,count($file)-5); // strip off ".php"
}, $this->documentFiles);
}
And then the instantiation test would be:
public function testInstantiation() {
foreach($this->documentClasses as $class) {
try {
$object = new $class;
} catch (Exception $e) {
fail("Failure to instantiate '{$class}': " + $e.getMessage());
}
}
}
right now the test is passing because the $CWD is not the right directory and consequently it isn't picking up any PHP files.
Is there a way to specify what the working directory should be? Ideally a way which is relative to the root of the project (and therefore abstracts the physical file system).
p.s. my project is PSR-0 compliant for what that is worth

I see at least 2 Unittests:
Testing that all php files with their classes are found
Testing that those classes are called as expected.
Usually you are also testing the filesystem with your tests which is a bad practice. Of course, your called classes need to be tested in separeted tests too in case you haven't done that.
I don't know your class structure so I can only guess you have at least created a class for reading the files fron the given directory, a class that gets the class names (which should be a piece of psr-0 cake) and a class creating the objects from that list. That way you can mock dependencies and the access to the filesystem is reduced to a simple class that can not influence other tests when its mocked.
I suggest using the DirectoryIterator or FilesystemIterator depending on which php version you are using.

Is there a way to specify what the working directory should be?
You can use chdir. You can also just pass a fully qualified path to glob:
glob(__DIR__ . '/*.php');
I don't think your solution is a good one, however. Use a data provider and return an array of instances (or class names as strings). You'll get much better error messages when you test and it won't break when/if you change directory structure.
<?php
class SomeExampleTest extends \PHPUnit_Framework_TestCase
{
public function nameProvider()
{
return array(
array(new Vendor\Package\ClassName()),
array(new Vendor\Package\ClassNameAgain()),
);
}
/**
* #dataProvider nameProvider
*/
public function testSomeMethod($obj)
{
// do some assertions
$this->assertEquals('something', $obj->theMethodYouWantToTest());
}
}

Ok, i've been able to achieve the result I was looking for but I'm wondering if there might be a more savvy "PHPunit" way of doing. Please let me know if you'd do this differently.
I identified that the CWD was the /tests/ directory. This is probably a little fragile as it could easily be run from somewhere further into the test chain but in most cases these days it's fine for me. I can then change my setUp() method to this:
protected function setUp() {
$namespace = "\\path\\to\\classes\\";
$this->documentFiles = glob('../src/path/to/classes/*.php');
$this->documentClasses = array_map(function($file) use ($namespace) {
if (preg_match('/.*\/(.*)\.php/',$file,$matches) === 1)
return $namespace . $matches[1];
else
return null;
}, $this->documentFiles);
}
As I said, it works. I'd love to think there is some more "built-in" way of doing something like this but I'm brand new to PHPunit.

Related

PHP - Design a stateless class

I want to design a stateless class, for reusing the same instance in several places of the application without have any side effect. This is necessary because many dependency injection containers always return the same instance of the class.
Let's assume we have the DirectoryUtils class:
<?php
class DirectoryUtils{
private $dir;
public function __constructor($dir){
if($this->checkDirectory($dir)){
$this->dir = $dir;
}else{
throw new Exception("Invalid Directory");
}
}
public function deleteAllFiles(){
// code to delete all files in $this->dir;
}
public function renameDirectory($newName){
// code to rename $this->dir name to $newName
}
private function checkDirectory($dir){
// check if directory exists, is writable...
}
}
A single instance of this class is uncomfortable to reuse. How to make it stateless?
I think you need to widen the scope you are investigating here: what is a meaningful service you can put in the DI container for reuse?
The problem with your class is not that it's stateful, but that its name reflects a misunderstanding of its purpose. This class represents a specific directory so should be named appropriately, e.g.
$my_home_dir = new Directory('/home/imsop');
$your_home_dir = new Directory('/home/abskmj');
$diff_tool->compareDirs($my_home_dir, $your_home_dir);
In your DI container, you would then put several specific instances of this class, for the specific directories you need to access, e.g.
[
'CacheDir' => function($config) {
return new Directory($config['cache_path']);
},
'LogDir' => function($config) {
return new Directory($config['log_path']);
}
]
Another type of service you might create is a factory, which abstracts part of the file system, and gives you different Directory objects, e.g.:
$my_home_dir = $factory->getHomeDir('imsop');
Here, the state constructed in the DI might be a base directory to work within:
[
'DirectoryFactory' => function($config) {
return new DirectoryFactory($config['base_dir_path']);
}
]
Neither of these classes is "stateless", but both are immutable - once constructed, you do not change them to point at a different directory, you use a different instance for each purpose.
The DirectoryFactory class could in fact be stateless by having all the logic hard-coded (e.g. "a home directory is always '/home/' . $user_name"). This could still be useful to put in the DI container, because you could substitute a version with different hard-coded rules, or a test mock. However, as shown, the lack of state is not a necessary pre-condition for it being useful as a "singleton" in this sense.
How to make it stateless
You do not talk about 'stateless' here. See https://en.wikipedia.org/wiki/Stateless
Make your functions static: https://secure.php.net/manual/en/language.oop5.static.php
i.e.
class DirectoryUtils {
...
public static function renameDirectory($newName) {
...
}
}
then you simply call it like this:
DirectoryUtils::renameDirectory($name);
The basic rule for a stateless class is not having any instance variables/properties to store the state. The class methods are similar to utility or helper function working on the arguments only.
Rewriting your class:
<?php
class DirectoryUtils{
public function deleteAllFiles($dir){
// code to delete all files in $dir;
}
public function renameDirectory($dir, $newName){
// code to rename $dir name to $newName
}
}

Way to change results of object's methods

I was trying to find a way to execute some code to alter the results of an objects methods without actually touching the object's code. One way I came up is using a decorator:
class Decorator {
private $object;
public function __construct($object) {
if (!is_object($object)) {
throw new Exception("Not an object");
}
$this->object = $object;
}
protected function doSomething(&$val) {
$val .= "!!";
}
public function __call($name, $arguments) {
$retVal = call_user_func_array(array($this->object, $name), $arguments);
$this->doSomething($retVal);
return $retVal;
}
}
class Test extends BaseTest {
public function run() {
return "Test->run()";
}
}
$o = new Decorator(new Test());
$o->run();
That way it will work properly but it has one disadvantage which makes it unusable for me right now - it would require replacing all lines with new Test() with new Decorator(new Test()) and this is exactly what I would like to avoid - lots of meddling with the existing code. Maybe something I could do in the base class?
One does not simply overload stuff in PHP. So what you want cannot be done. But the fact that you are in trouble now is a big tell your design is flawed. Or if it is not your code design the code you have to work with (I feel your pain).
If you cannot do what you want to do it is because you have tightly coupled your code. I.e. you make use of the new keyword in classes instead of injecting them (dependency injection) into the classes / methods that need it.
Besides not being able to easily swap classes you would also have a gard time easily testing your units because of the tight coupling.
UPDATE
For completeness (for possible future readers): if the specific class would have been namespaced and you were allowed to change the namespace you could have thought about changing the namespace. However this is not really good practice, because it may screw with for example autoloaders. An example of this would be PSR-0. But considering you cannot do this either way I don't see it is possible what you want. P.S. you should not really use this "solution".
UPDATE2
It looks like there has been some overload extension at some time (way way way back), but the only thing I have found about it is some bug report. And don't count on it still working now either way. ;-) There simply is no real overloading in PHP.
Found something (a dead project which doesn't work anymore that enables class overloading): http://pecl.php.net/package/runkit
Possibly another project (also dead of course): http://pecl.php.net/package/apd
I am not a PHP programmer, but I think that AOP is what you are looking for. You can try some frameworks, for example listed in this answer.
From the Wikipedia article on the decorator pattern:
Subclass the original "Decorator" class into a "Component" class
So I think you're supposed to keep the class to be decorated private and expose only the already-decorated class.

PHP Class modeling issue

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.

Adapter pattern with static classes

I am looking for a good way to implement the Adaptor pattern with static classes in PHP 5.x.
One of the examples where I would like to use this, is as a counterpart to Python's os.path.join().
I would have two adaptees, a Windows and a Linux adaptee class.
I think it is reasonable, to implement these classes as static classes, because they have no "context". They do not need to store any state and creating an instance everytime I need one seems superfluous - therefore I am looking for a clean way to implement this.
Let's consider the following bogus implementation:
static public function join(){
$parts = func_get_args();
$joined = array(MY_MAGICALLY_PASSED_DEFAULT_PATH_PREFIX);
foreach($parts as $part){
$part = self::$adaptee->cleanPath($path);
if(self::$adaptee->isAbsolute($part)){
$joined = array($part);
}
else{
$joined[] = $part;
}
}
return implode(PATH_SEPARATOR, $joined);
}
The first thing you will notice is, that it assumes an initialized static member called adaptee which would hold the necessary, OS-dependent implementation details.
This requires me to have an arbitrarily named static constructor-like function, that I would call immediately after the declaration of the class. (Another thing that bothers me with this approach).
Of course, I could initialize a local $adaptee variable on each method call, but that seems like inappropriate and I would have to replicate that in each other static function that needs the adaptee.
Now... for PHP's class implemention detail: They are not first-class objects, so I couldn't just pass the class as an argument. In the example, it requires me to create the Adaptees as non-static (what is the term for this?) classes, then instantiate it and eventually assign it to the static $adaptee member variable of the Adapter class.
Maybe this is just this weird and completely subjective thought that I have... but I really feel that it is not appropriate to do it like this. Do you have any ideas about a better implementation?
One other idea that I've had is, to store the adaptee's class name instead, and use call_user_func instead, but I don't feel too comfortable using this approach.
Update
I may not have described this properly, so I will try to explain this in an update:
I am not looking on how to get the underlying Operating System, but I would like to have a neat way, for a static class to act differently depending on whether the OS is Linux, Windows, FreeBSD or something else.
I thought of the adaptor pattern, but since I don't have a static constructor, I cannot really initialize the class. One way would be to initialize it at the beginning of every public static method call (or just check whether it is initialized).
The other possibility would be, to create a static constructor-like method and simply call it right after the declaration. That might do the trick, but I am just wondering what other, possibly more elgeant methods there are, to achieving this.
As for my initial example:
It is supposed to be a utility function, it does not need to preserve state in any kind really, so I am not looking for a Path-Object of any sorts. What I would like, is a Path factory function, that returns a string, without having to differentiate between the different OSes every time when called. The "library"-thing led me to create a static class as pseudo-namespace for my related utility functions, and the different implementation details that need to be supported to the adaptor pattern. Now I am looking for an elegant way, to combine the two.
You'll shoot yourself in the foot when you make them static. You cannot inject static classes so you will always have coupling to the global scope and because you will hardcode static calls everywhere, maintaining them will become a nightmare. And you cannot mock them either (ok, PHPUnit can, but it only does to enable testing of code that otherwise would be untestable).
Just create an instance and use regular functions and save yourself some worries. There is no advantage in using statics. And the performance impact is utterly and totally negligible.
I'd probably create an interface for the adaptee and the adapters to implement
interface IPathAdapter
{
public function cleanPath($path);
public function isAbsolutePath($part);
// more …
}
and then do probably something like
class Path implements IPathAdapter
{
protected $_adapter;
public function __construct(IPathAdapter $adapter)
{
$this->_adapter = $adapter;
}
public function cleanPath($path)
{
$this->_adapter->cleanPath($part);
}
public function isAbsolutePath($part)
{
$this->_adapter->isAbsolutePath($part);
}
// more …
public function join(){
$parts = func_get_args();
$joined = array($this->getScriptPath());
foreach($parts as $part){
$part = $this->cleanPath($path);
if ($this->isAbsolutePath($part)){
$joined = array($part);
} else{
$joined[] = $part;
}
}
return implode($this->getPathSeparator(), $joined);
}
}
So when I want to use Path, I'd have to do
$path = new Path(new PathAdapter_Windows);
If you cannot inject the adapters, I'd probably go the route you already suggested and pass the Adapter class name as an argument to instantiate it from within Path then. Or I'd leave the detection of the appropriate adapter completely to the Path class, e.g. have it detect the OS and then instantiate what is needed.
If you want to autodetect, have a look at Does PHP have a function to detect the OS it's running on?. I'd probably write a separate class to handle the identification and then make it a dependency to the Path class, e.g.
public function __construct(IDetector $detector = NULL)
{
if($detector === NULL){
$detector = new OSDetector;
}
$this->_detector = $detector;
}
The reason I am injecting is because it will allow me to change the implementation, e.g. to mock the Detector in UnitTests but can also ignore to inject at runtime. It will use the default OSDetector then. With the detector, detect the OS and create an appropriate adapter somewhere in Path or in a dedicated Factory.
I think you can do this, you just have to put the namespace path into a global var, for example in composer autoload.php:
$GLOBALS['ADAPTED_CLASS_NAMESPACE'] = 'MyComponent\AdapterFoo\VendorBar';
I think it's a good approach in a context where you can't use dependency injection i.e in a entity for validation (we keep in mind that separated Validation classes are better).
<?php
namespace MyComponent;
use MyComponent\AdaptedInterface;
use ReflectionClass;
class Adapter
{
/**
* #var AdaptedInterface
*/
protected $adaptedClass;
public function __construct(AdaptedInterface $validator = null)
{
if (null == $validator && $this->validateClassPath($GLOBALS['ADAPTED_CLASS_NAMESPACE'])) {
$this->adaptedClass = new $GLOBALS['ADAPTED_CLASS_NAMESPACE'];
} else {
$this->adaptedClass = $validator;
}
}
protected function validateClassPath($classPath)
{
$reflection = new ReflectionClass($classPath);
if (!$reflection->implementsInterface(
'MyComponent\AdaptedInterface'
)) {
throw new \Exception('Your adapted class have not a valid class path :' . $classPath . 'given');
}
return true;
}
}
So anywhere:
(new Adapter())->foo($bar);

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