PHP - Design a stateless class - php

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
}
}

Related

Scalable way of implementing the Registry pattern in PHP?

I was wondering if there's a good way to implement the registry pattern in PHP, let me be more clear:
I do know that a Registry is used when you need to keep track of the object you instantiate in order to reuse them and not re-instantiate them again from script to script, e.g. I have a Database class that I want to instantiate only once and then use for all my scripts and I do not want to re-instantiate it again and again. Another example could be a User class that represents an instance of the currently logged in user. I could not use a Singleton in this case, cause e.g. I need another User instance for example when I want to retrieve a friend of the currently logged in user etc.
So I came up with the idea that the Registry better suits this kind of needs in such cases.
I also know that there are two ways of implementing it, or better two ways in order to access the stored instances:
Explicitly or externally, meaning that the Registry should be called every time you need to recover an instance inside your scripts or you need to put an instance inside of it;
Implicitly or internally, meaning that you make kind of an abstract class with a getInstance() method that returns an instance with the get_called_class() late static binding feature, adds it to the registry and then return that instance from the registry itself taking care that if a $label parameter is passed to the getInstance() method, then that particular instance from the registry will be returned. This approach is kinda transparent to the consumer and in my opinion is cleaner and neater (I'll show both implementations, though).
Let's take a basic Registry (really simple implementation, just an example took from a book):
class Registry {
static private $_store = array();
static public function set($object, $name = null)
{
// Use the class name if no name given, simulates singleton
$name = (!is_null($name)) ? $name: get_class($object);
$name = strtolower($name);
$return = null;
if (isset(self::$_store[$name])) {
// Store the old object for returning
$return = self::$_store[$name];
}
self::$_store[$name]= $object;
return $return;
}
static public function get($name)
{
if (!self::contains($name)) {
throw new Exception("Object does not exist in registry");
}
return self::$_store[$name];
}
static public function contains($name)
{
if (!isset(self::$_store[$name])) {
return false;
}
return true;
}
static public function remove($name)
{
if (self::contains($name)) {
unset(self::$_store[$name]);
}
}
}
I know, Registry could be a Singleton, so you never have two Registry at the same time (who needs them someone could think, but who knows).
Anyway the externally way of storing/accessing instances is like this:
$read = new DBReadConnection;
Registry::set($read);
$write = new DBWriteConnection;
Registry::set($write);
// To get the instances, anywhere in the code:
$read = Registry::get('DbReadConnection');
$write = Registry::get('DbWriteConnection');
And internally, inside the class (taken from the book) when getInstance is called:
abstract class DBConnection extends PDO {
static public function getInstance($name = null)
{
// Get the late-static-binding version of __CLASS__
$class = get_called_class();
// Allow passing in a name to get multiple instances
// If you do not pass a name, it functions as a singleton
$name = (!is_null($name)) ?: $class;
if (!Registry::contains($name)) {
$instance = new $class();
Registry::set($instance, $name);
}
return Registry::get($name);
}
}
class DBWriteConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_WRITE_DSN, APP_DB_WRITE_USER, APP_DB_WRITE_PASSWORD);
} }
class DBReadConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_READ_DSN, APP_DB_READ_USER,APP_DB_READ_PASSWORD);
}
}
Apparently referring to the registry indirectly (second case) seems more scalable for me, but what if some day I would need to change the registry and use another implementation, I would need to change that calls to Registry::get() and Registry::set() inside the getInstance() method in order to suit the changes or is there a smarter way?
Did someone of you came across this problem and found an easy way to interchange different registries depending on the type of application on the complexity etc.?
Should be a configuration class the solution? Or is there a smarter way to achieve a scalable registry pattern if it is possible?
Thanks for the attention! Hope for some help!
First of all. It's great that you spotted the problem of your approach by yourself. By using a registry you are tight coupling your classes to the registry where you pull your dependencies from. Not only that, but if your classes have to care about how they are stored in the registry and get grabbed from it (in your case every class would also implement a singleton), you also violate the Single-Responsibility-Principle.
As a rule of thumb keep in mind: Accessing objects globally from within a class from whatever storage will lead to tight coupling between the class and the storage.
Let's see what Martin Fowler has to say about this topic:
The key difference is that with a Service Locator every user of a service has a dependency to the locator. The locator can hide dependencies to other implementations, but you do need to see the locator. So the decision between locator and injector depends on whether that dependency is a problem.
and
With the service locator you have to search the source code for calls to the locator. Modern IDEs with a find references feature make this easier, but it's still not as easy as looking at the constructor or setting methods.
So you see it depends on what you are building. If you have a small app with a low amount of dependencies, to hell with it, go on with using a registry (But you absolutely should drop a classes behavior to store itself into or getting grabbed from the registry). If that's not the case and you are building complex services and want a clean and straightforward API define your dependencies explicitly by using Type Hints and Constructor Injection.
<?php
class DbConsumer {
protected $dbReadConnection;
protected $dbWriteConnection;
public function __construct(DBReadConnection $dbReadConnection, DBWriteConnection $dbWriteConnection)
{
$this->dbReadConnection = $dbReadConnection;
$this->dbWriteConnection = $dbWriteConnection;
}
}
// You can still use service location for example to grab instances
// but you will not pollute your classes itself by making use of it
// directly. Instead we'll grab instances from it and pass them into
// the consuming class
// [...]
$read = $registry->get('dbReadConnection');
$write = $registry->get('dbWriteConnection');
$dbConsumer = new DbConsumer($read, $write);
Should be a configuration class the solution? Or is there a smarter way to achieve a scalable registry pattern if it is possible?
That approach is encountered very often and you maybe have heard something about a DI-Container. Fabien Potencier writes the following:
A Dependency Injection Container is an object that knows how to instantiate and configure objects. And to be able to do its job, it needs to knows about the constructor arguments and the relationships between the objects.
The boundaries between a service locator and a DI-Container seem to be pretty blurry but I like the concept to think about it like that: A Service Locator hides the dependencies of a class while a DI-Container does not (which comes along with the benefit of easy unit testing).
So you see, there is no final answer and it depends on what you are building. I can suggest to dig more into the topic since how dependencies are managed is a core concern of every application.
Further Reading
Why Registry Pattern is antipattern. And what is alternative for it.
Service Locator is an Anti-Pattern
Do you need a Dependency Injection Container?

Operating on all files/classes in a directory

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.

Alternatives to static methods in a framework PHP

Lately I have been trying to create my own PHP framework, just to learn from it (As we may look into some bigger and more robust framework for production). One design concept I currently have, is that most core classes mainly work on static functions within classes.
Now a few days ago, I've seen a few articles about "Static methods are death to testability". This concerned me as.. yeah.. my classes contain mostly static methods.. The main reason I was using static methods is that a lot of classes would never need more than one instance, and static methods are easy to approach in the global scope. Now I'm aware that static methods aren't actually the best way to do things, I'm looking for a better alternative.
Imagine the following code to get a config item:
$testcfg = Config::get("test"); // Gets config from "test"
echo $testcfg->foo; // Would output what "foo" contains ofcourse.
/*
* We cache the newly created instance of the "test" config,
* so if we need to use it again anywhere in the application,
* the Config::get() method simply returns that instance.
*/
This is an example of what I currently have. But according to some articles, this is bad.
Now, I could do this the way how, for example, CodeIgniter does this, using:
$testcfg = $this->config->get("test");
echo $testcfg->foo;
Personally, I find this harder to read. That's why I would prefer another way.
So in short, I guess I need a better approach to my classes. I would not want more than one instance to the config class, maintain readability and have easy access to the class. Any ideas?
Note that I'm looking for some best practice or something including a code sample, not some random ideas. Also, if I'm bound to a $this->class->method style pattern, then would I implement this efficiently?
In response to Sébastien Renauld's comments: here's an article on Dependency Injection (DI) and Inversion of Control (IoC) with some examples, and a few extra words on the Hollywood principle (quite important when working on a framework).
Saying your classes won't ever need more than a single instance doesn't mean that statics are a must. Far from it, actually. If you browse this site, and read through PHP questions that deal with the singleton "pattern", you'll soon find out why singletons are a bit of a no-no.
I won't go into the details, but testing and singletons don't mix. Dependency injection is definitely worth a closer look. I'll leave it at that for now.
To answer your question:
Your exaple (Config::get('test')) implies you have a static property in the Config class somewhere. Now if you've done this, as you say, to facilitate access to given data, imagine what a nightmare it would be to debug your code, if that value were to change somewhere... It's a static, so change it once, and it's changed everywhere. Finding out where it was changed might be harder than you anticipated. Even so, that's nothing compared to the issues someone who uses your code will have in the same situation.
And yet, the real problems will only start when that person using your code wants to test whatever it is he/she made: If you want to have access to an instance in a given object, that has been instantiated in some class, there are plenty of ways to do so (especially in a framework):
class Application
{//base class of your framework
private $defaulDB = null;
public $env = null;
public function __construct($env = 'test')
{
$this->env = $env;
}
private function connectDB(PDO $connection = null)
{
if ($connection === null)
{
$connection = new PDO();//you know the deal...
}
$this->defaultDB = $connection;
}
public function getDB(PDO $conn = null)
{//get connection
if ($this->defaultDB === null)
{
$this->connectDB($conn);
}
return $this->defaultDB;
}
public function registerController(MyConstroller $controller)
{//<== magic!
$controller->registerApplication($this);
return $this;
}
}
As you can see, the Application class has a method that passes the Application instance to your controller, or whatever part of your framework you want to grant access to scope of the Application class.
Note that I've declared the defaultDB property as a private property, so I'm using a getter. I can, if I wanted to, pass a connection to that getter. There's a lot more you can do with that connection, of course, but I can't be bothered writing a full framework to show you everything you can do here :).
Basically, all your controllers will extend the MyController class, which could be an abstract class that looks like this:
abstract class MyController
{
private $app = null;
protected $db = null;
public function __construct(Application $app = null)
{
if ($app !== null)
{
return $this->registerApplication($app);
}
}
public function registerApplication(Application $app)
{
$this->app = $app;
return $this;
}
public function getApplication()
{
return $this->app;
}
}
So in your code, you can easily do something along the lines of:
$controller = new MyController($this);//assuming the instance is created in the Application class
$controller = new MyController();
$controller->registerApplication($appInstance);
In both cases, you can get that single DB instance like so:
$controller->getApplication()->getDB();
You can test your framework with easily by passing a different DB connection to the getDB method, if the defaultDB property hasn't been set in this case. With some extra work you can register multiple DB connections at the same time and access those at will, too:
$controller->getApplication->getDB(new PDO());//pass test connection here...
This is, by no means, the full explanation, but I wanted to get this answer in quite quickly before you end up with a huge static (and thus useless) codebase.
In response to comments from OP:
On how I'd tackle the Config class. Honestly, I'd pretty much do the same thing as I'd do with the defaultDB property as shown above. But I'd probably allow for more targeted control on what class gets access to what part of the config:
class Application
{
private $config = null;
public function __construct($env = 'test', $config = null)
{//get default config path or use path passed as argument
$this->config = new Config(parse_ini_file($config));
}
public function registerController(MyController $controller)
{
$controller->setApplication($this);
}
public function registerDB(MyDB $wrapper, $connect = true)
{//assume MyDB is a wrapper class, that gets the connection data from the config
$wrapper->setConfig(new Config($this->config->getSection('DB')));
$this->defaultDB = $wrapper;
return $this;
}
}
class MyController
{
private $app = null;
public function getApplication()
{
return $this->app;
}
public function setApplication(Application $app)
{
$this->app = $app;
return $this;
}
//Optional:
public function getConfig()
{
return $this->app->getConfig();
}
public function getDB()
{
return $this->app->getDB();
}
}
Those last two methods aren't really required, you could just as well write something like:
$controller->getApplication()->getConfig();
Again, this snippet is all a bit messy and incomplete, but it does go to show you that you can "expose" certain properties of one class, by passing a reference to that class to another. Even if the properties are private, you can use getters to access them all the same. You can also use various register-methods to control what it is the registered object is allowed to see, as I've done with the DB-wrapper in my snippet. A DB class shouldn't deal with viewscripts and namespaces, or autoloaders. That's why I'm only registering the DB section of the config.
Basically, a lot of your main components will end up sharing a number of methods. In other words, they'll end up implementing a given interface. For each main component (assuming the classic MVC pattern), you'll have one abstract base-class, and an inheritance chain of 1 or 2 levels of child classes: Abstract Controller > DefaultController > ProjectSpecificController.
At the same time, all of these classes will probably expect another instance to be passed to them when constructed. Just look at the index.php of any ZendFW project:
$application = new Zend_Application(APPLICATION_ENV);
$application->bootstrap()->run();
That's all you can see, but inside the application, all other classes are being instantiated. That's why you can access neigh on everything from anywhere: all classes have been instantiated inside another class along these lines:
public function initController(Request $request)
{
$this->currentController = $request->getController();
$this->currentController = new $this->currentController($this);
return $this->currentController->init($request)
->{$request->getAction().'Action'}();
}
By passing $this to the constructor of a controller class, that class can use various getters and setters to get to whatever it needs... Look at the examples above, it could use getDB, or getConfig and use that data if that's what it needs.
That's how most frameworks I've tinkered or worked with function: The application is kicks into action and determines what needs to be done. That's the Hollywood-principle, or Inversion of Control: the Application is started, and the application determines what classes it needs when. In the link I provided I believe this is compared to a store creating its own customers: the store is built, and decides what it wants to sell. In order to sell it, it will create the clients it wants, and provide them with the means they need to purchase the goods...
And, before I forget: Yes, all this can be done without a single static variable, let alone function, coming into play. I've built my own framework, and I've never felt there was no other way than to "go static". I did use the Factory pattern at first, but ditched it pretty quickly.
IMHO, a good framework is modular: you should be able to use bits of it (like Symfony's components), without issues. Using the Factory pattern makes you assume too much. You assume class X will be available, which isn't a given.
Registering those classes that are available makes for far more portable components. Consider this:
class AssumeFactory
{
private $db = null;
public function getDB(PDO $db = null)
{
if ($db === null)
{
$config = Factory::getConfig();//assumes Config class
$db = new PDO($config->getDBString());
}
$this->db = $db;
return $this->db;
}
}
As opposed to:
class RegisteredApplication
{//assume this is registered to current Application
public function getDB(PDO $fallback = null, $setToApplication = false)
{
if ($this->getApplication()->getDB() === null)
{//defensive
if ($setToApplication === true && $fallback !== null)
{
$this->getApplication()->setDB($fallback);
return $fallback;//this is current connection
}
if ($fallback === null && $this->getApplication()->getConfig() !== null)
{//if DB is not set #app, check config:
$fallback = $this->getApplication()->getConfig()->getSection('DB');
$fallback = new PDO($fallback->connString, $fallback->user, $fallback->pass);
return $fallback;
}
throw new RuntimeException('No DB connection set #app, no fallback');
}
if ($setToApplication === true && $fallback !== null)
{
$this->getApplication()->setDB($fallback);
}
return $this->getApplication()->getDB();
}
}
Though the latter version is slightly more work to write, it's quite clear which of the two is the better bet. The first version just assumes too much, and doesn't allow for safety-nets. It's also quite dictatorial: suppose I've written a test, and I need the results to go to another DB. I therefore need to change the DB connection, for the entire application (user input, errors, stats... they're all likely to be stored in a DB).
For those two reasons alone, the second snippet is the better candidate: I can pass another DB connection, that overwrites the application default, or, if I don't want to do that, I can either use the default connection, or attempt to create the default connection. Store the connection I just made, or not... the choice is entirely mine. If nothing works, I just get a RuntimeException thrown at me, but that's not the point.
Magic methods would help you: see the examples about __get() and __set()
You should also take a look at namespaces: it may help you to get rid of some classes with static methods only.

How to test a Factory / Strategy implementation with PHPUnit

I have a Factory class that returns a writer strategy based on the extension of a given file:
public static function getWriterForFile($file)
{
// create file info object
$fileInfo = new \SplFileInfo($file);
// check that an extension is present
if ('' === $extension = $fileInfo->getExtension()) {
throw new \RuntimeException(
'No extension found in target file: ' . $file
);
}
// build a class name using the file extension
$className = 'MyNamespace\Writer\Strategy\\'
. ucfirst(strtolower($extension))
. 'Writer';
// attempt to get an instance of the class
if (!in_array($className, get_declared_classes())) {
throw new \RuntimeException(
'No writer could be found for the extension: ' . $extension
);
}
$instance = new $className();
$instance->setTargetFile($file);
return $instance;
}
All of my strategies implement the same interface, for example:
class CsvWriter implements WriterStrategyInterface
{
public function setTargetFile($file)
{
...
}
public function writeLine(array $data)
{
...
}
public function flush()
{
...
}
}
I want to be able to test this method using a dummy extension so that my tests are not dependant on any particular strategy existing. I tried creating a mock for the interface with a set classname, but this does not seem to have declared the mock class name:
public function testExpectedWriterStrategyReturned()
{
$mockWriter = $this->getMock(
'MyNamespace\Writer\Strategy\WriterStrategyInterface',
array(),
array(),
'SssWriter'
);
$file = 'extension.sss';
$writer = MyNamespace\Writer\WriterFactory::getWriterForFile($file);
$this->assertInstanceOf('MyNamespace\Writer\Strategy\WriterStrategyInterface', $writer);;
}
Is there any way for me to stage a mock writer strategy for the factory to load, or should I refactor the factory method to make it more testable?
The main purpose and responsibility for that factory is the creation of objects.
Imho you should test that using
$this->assertInstanceOf('class', $factory->getWriterForFile($file));
Why not abstract further?
You could beimplementation in a way so that the factory dispatches the object creation to another class.
Having a "object creator" that does:
$class = new ReflectionClass($class);
$instance = $class->newInstanceArgs($args);
or something along those lines but what is your gain apart from "well the test looks more like a unit test"? You're not improving really improving your code base and changes just for test-abilities sake always smell a little funny to me.
I'd treat factory tests as integration/wiring tests that make sure all your factory actually work and produce the desired objects.
The only change I'd suggest for your current code sample would be to change two things:
a) make the factory method non static
If you don't have a very good reason you don't gain anything from a static factory except that you can't inject it and making it static makes it accessible from global scope so getting dependencies into the factory will require even more globals and so on. If you do DI with factories usually making them proper objects too helps avoiding issues down the road
b) Don't do diskIo in the factory. Use: getWriterForFile(\SplFileInfo $file)
The factory should not care about the file system. If it needs a existing file it should require one and leave the details of how to handle the error to the consumers.
That gives you the benefit that you can also pass a SplTempFileObject which will make testing easier and allow your factories to be independent of the file system even for production purposes.

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);

Categories