Recently I have been told that static class/methods are evil.
Take for example my class Logger:
class Logger{
private static $logs = array();
public static function add($msg){
self::$logs[]=$msg;
}
public static function echo(){
print_r(self::$logs);
}
}
I can use whenever i want in my appliaction like this:
Logger::add('My log 1');
But reading this developers:
http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/
That Logger class doesn't seem so good.
So: Can I use it statically or I should avoid it at any cost?
Logging classes are the exception.
Since they rarely contain much logic, you don't have the same testing concerns.
Logging is a perfect example of a GOOD place to use static classes.
Think of your alternatives:
A global instance of a logging object?
A singleton logging object?
Pass the logging object around to every single method/class (via in a constructor)?
The above are much worse than using static for logging.
Avoid it. I've seen quite some posts of you now struggling with the issue and people giving you bad advice. I'll repeat what I said in some of my answers/comments.
The way you use static in your logger class is to use it as a globally access point. Whenever you need to logg something you call Logger::log().
1) You will not be able to tell from looking at your class definition that it depends on the Logger class. Change in code thus becomes an adventure: 'I hope I will not break some hidden dependency when I change this tiny little ... OOPS!'.
2) It IS harder to test. You can't realiably test a class that sends a message to the Logger with Logger::log(). When a test fails how will you know it is not because the Logger fails? You would know if you could replace it with a mock, but in your case it is not mockable.
An alternative to explore:
Use the observer pattern and make the Logger an observer, the classes that need logging can be observables. They send messages like $this->observers->nofify('test succeeded').
You could use some other form of events too or dependency injection (automatic or manual). But please please don't call Logger::log() in a method.
I still think that logging is a valid approach to use a static classes. The often stated phrase that it is not testable is imho also not true if you do it right. I want to implement this but did not find the time, however, I thought about something like the following.
class Logger {
protected static $handlerSet = [];
// Pure static class {{{
private function __construct() {}
private function __clone() {}
private function __sleep() {}
private function __wakeup() {}
// }}}
public static function critical($message, array $context = []) {}
// You know the PSR drill...
private static function log($level, $message, array $context) {
foreach ($this->handlerSet as $handler) {
$handler->handle($level, $message, $context);
}
}
}
Of course we do not want to expose the management of the handlers to all classes, hence, we use a child class that has access to the protected handler set.
final class LoggingManager extends Logger {
public static function addHandler(Handler $handler, $name, $level) {
static::$handlerSet[$name] = $handler;
}
public static function removeHandler($name) {
if (isset(static::$handlerSet[$name])) {
unset(static::$handlerSet[$name]);
}
}
public static function resetHandlers() {
static::$handlerSet = [];
}
// Other useful stuff...
}
Testing is now fairly easy, if you actually want to test something like logging (could be that it has some ROI for you, don’t know).
class SomeTest extends YourFrameworksTestCase {
public function testThatSomethingLogsSomething() {
try {
$handler = new TestLogHandler();
LoggingManager::registerHandler($handler, 'test', 'debug');
// Test something.
$this->assertLogRecordExists($handler, '[debug] StackOverflow');
}
finally {
LoggingManager::resetHandlers();
}
}
}
It would also be possible to create a more sophisticated test case to extend that implements all of the log record assertion for you. This approach is imho fairly easy and a class in your system should not care whether a handler is registered or not, nor what it does with the logged messages. Things like that are handled in your application and only there. The advantages are obvious:
Global access to the logger and logging manager.
Easy testing, comparable to dependency injection.
No need for code polluting DIC solutions.
Single logger instance, always.
…
While there is nothing wrong with that approach, I recently moved from a static logging class approach to log4php in one of my own projects myself.
log4php uses a separate instance of a logging class for each class in your project. When looking at that logging framework, the benefits become obvious.
Logged messages always have a context (the class through which the message was logged). That allows for easy filtering (and make the log slightly more helpful).
The only problem with static classes is that they are hard to change.
So it's ok here since you're class doesn't do much.
Related
Through my multiple studies I have come across the factory method of setting session and database objects which I have been using while in development. What I am wondering is, putting aside personal preference (although I will soak in any opinions anyone has), does this general method work, and is it efficient (meaning, am I using it correctly)? If it is not, do you have suggestions for how to improve it?
Background
I created the code this way so as to pass a database and session object to the class upon calling the class. I wanted to be able to pass along the relevant objects/references so that they could be used.
The Call Class
This class is meant to call static functions, like so:
class CALL {
public static $_db, $_session;
public status function class1() {
$function = new class1();
$function->set_session(self::$_session);
$function->set_database(self::$_db);
return $function;
}
public status function class2() {
...
}
...
}
The _set class
class _set {
public $_db, $_session;
public function __construct() { ... }
public function set_database($_db) {
$this->_db = $_db;
}
public function set_session($_session) {
$this->_session = $_session;
}
}
Now the classes referenced.
class class1 extends _set {
function __construct() { ... }
function function1() { return "foo"; }
...
}
So, moving forward, the classes would be called using CALL::class1 or CALL::class2. After that, they can be accessed as per usual, aka:
CALL::$_db = $database->_dbObject;
CALL::$_session = $_SESSION;
$class1 = CALL::class1;
echo $class1->function1(); //prints "foo".
Read about Dependency Injection . Small suggestion from my point of view, you should never create objects like $db or $session inside other objects. You should rather inject them through constructor or setter method. It will make your code less dependant on a specific classes and it will be easier to replace all dependencies almost without refactoring (actually without one if you know hot to use interfaces).
If anyone stumbles on this, I will share with you what my solution was.
Although this exercise helped me to learn a lot, and I am sure I could take the time to create a VERY highly functional factory/Container, because this is not integral to my program and not even unique, I finally bowed to the age old wisdom of not repeating something that has already been done.
I utilized Pimple, a lightweight library that uses PHP closures to create function calls. Now, I can haave the flexibility of determining which dependency injections I want, but I also only need to inject them once. Future calls, even when they create new instances, will replicate them. While I think that, in theory, my project was workable as it was, it did indeed have the unfortunate issue of requiring you to go into the container to make changes. With Pimple I do not need to do that. So I've tossed by Container class and picked up a lightweight program from the maker of Symfony. While this may not be the best answer for everyone, it was for me. Cheers!
My static methods are either of the 'helper' variety, e.g. convertToCamelCase(), or of the 'get singleton' variety, e.g. getInstance(). Either way, I am happy for them to live in a helper class.
The helper class needs to be widely available so I am loading it in my layer supertypes. Now, as far as I can see, provided that the helper can be injected into the supertypes, I have maintained full flexibility over testing my code (with the exception of the helper class itself). Does that make sense? Or am I overlooking something?
To look at it another way... it seems to me that difficulty in testing code increases in proportion to the number of calls to static methods, not in proportion to actual number of static methods themselves. By putting all these calls into one class (my helper), and replacing that class with a mock, I am testing code that is free of static calls and related problems.
(I realise that I should work towards getting rid of my Singletons, but that's going to be a longer term project).
In the case of a static class that is strictly a helper function like "convertToCamelCase" I would probably just have 100% coverage for that function and then consider it a "core" function and not worry about mocking it elsewhere. What is a mock for "convertToCamelCase" going to do anyway..? Perhaps your unit tests start to smell a little like integrations tests if you do it too much, but there's always a bit of a tradeoff between abstracting everything and making your app needlessly complicated.
As far as singletons it is tricky because you usually have the name of the static class in your code so it becomes problematic to swap it out with a mock object for testing. One thing you could do is wherever you are making your static method calls, start by refactoring to call them this way:
$instance = call_user_func('MyClass::getinstance');
Then, as you increase your testing coverage, you could begin replacing with something like:
$instance = call_user_func($this->myClassName . '::getinstance');
So - once you have that, you could swap out and mock MyClass by changing $this->myClassName. You'd have to make sure you're requiring or autoloading the relevant php files dynamically as well.
Refactoring to use an abstract factory pattern would make things even easier to test but you could start implementing that over time as well
However if you need to mock a static class somewhere, you can do it with Moka library. Here is example:
class UsersController
{
public function main()
{
return json_encode(User::find(1));
}
}
This is how you can test it:
class UsersController
{
private $_userClass;
public function __construct($userClass = 'User')
{
$this->_userClass = $userClass;
}
public function find($id)
{
return json_encode($this->_userClass::find($id));
}
}
class UsersControllerTest extends \PHPUnit_Framework_TestCase
{
public function testMainReturnsUser()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$this->assertEquals('"USER"', $controller->find(1000));
}
public function testMainCallsFind()
{
$userClass = Moka::stubClass(null, ['::find' => 'USER']);
$controller = new UsersController($userClass);
$controller->find(1000);
// check that `find` was called with 100
$this->assertEquals([1000], $userClass::$moka->report('find')[0]);
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Dependency Hell — how does one pass dependencies to deeply nested objects?
In a system built around strong dependency injection, I'm wondering how to deal with a contrived situation like this:
<?php
class LogWriter
{
public function write(Log $log)
{
echo $log->getMessage();
}
}
class Log
{
private $message;
public function setMessage($message)
{
$this->message = $message;
}
public function getMessage()
{
return $this->message;
}
}
class Logger
{
private $writer;
public function __construct(LogWriter $writer)
{
$this->writer = $writer;
}
public function write($message)
{
// Here is the dependency
$log = new Log();
$log->setMessage($message);
$this->writer->write($log);
}
}
The Logger::write() method creates an instance of Log, and passes it to the log writer. My gut tells me that's a bad approach, and a month from now I'm going to be tracking down a bug related to it, and I might want to switch the Log class for something else during testing.
But how to avoid it? The only thing that comes to mind is passing a Log type to the Logger constructor, and changing my Logger class to this:
class Logger
{
private $writer;
private $log_type;
public function __construct(LogWriter $writer, $log_type)
{
$this->writer = $writer;
$this->log_type = $log_type;
}
public function write($message)
{
$log = new $this->log_type();
$log->setMessage($message);
$this->writer->write($log);
}
}
And then creating a new Logger instance like this:
$log_writer = new LogWriter();
$logger = new Logger($log_writer, "Log");
But that feels a bit hackish. So how do you deal with micro-dependencies like this?
Note: I'm using the logging classes as an example, and I'm not looking for a solution to this exact problem. I would probably just use an array instead of the Log class.
Edit: In a more complex situation, I might pass a dependency injection container to the Logger class, and use that to create an instance of Log, but that seems overly complicated for a simple logger class.
Since your Log object is really just a Data Transfer Object or Value Object, you can create it inside the Logger class. It's okay to do so in this case. You dont need to pass anything to the Logger. But you are right in that you wont be able to mock/stub this easily then.
As an alternative, you could also inject a Factory if you want to decouple the Log class from the Logger:
$logger = new Logger($logWriter, new LogFactory);
and then create the Log Type from there:
public function write($message)
{
$log = $this->logFactory->createNew();
…
This capsules the creation logic inside the Factory class. The Factory will still have the Log type hardcoded inside, but it's okay for Factories to have that. You then just test that it returns the right type when you call createNew. And in your consumers, you can stub that call.
If you dont feel like creating a full-blown Factory class for this, you can also use a Lambda instead of a Factory. Since it captures the essential creation logic, it's effectively the same as a Factory, just without a class:
$logger = new Logger($logWriter, function() { return new Log; });
And then
public function write($message)
{
$log = call_user_func($this->createLogCallback);
…
Both, the Factoy and the Lambda approach allow for substituting the Log type in your Unit-Test. Then again, substituting the Log type doesn't seem that necessary in your scenario. The Log type doesn't have any dependencies of it's own, so you can pretty much use the real deal here. You can easily verify your write method by simply looking at what gets written by the LogWriter. You won't have an explicit assertion on a Log Mock, but if the writer produces the expected output for the given input to write, you can safely assume that the Log type collaborates as expected.
Also see http://misko.hevery.com/2008/09/30/to-new-or-not-to-new for more details.
Why is testing singletons or registry pattern hard in a language like PHP which is request driven?
You can write and run tests aside from the actual program execution, so that you are free to affect the global state of the program and run some tear downs and initialization per each test function to get it to the same state for each test.
Am I missing something?
While it's true that "you can write and run tests aside of the actual program execution so that you are free to affect the global state of the program and run some tear downs and initialization per each test function to get it to the same state for each test.", it is tedious to do so. You want to test the TestSubject in isolation and not spend time recreating a working environment.
Example
class MyTestSubject
{
protected $registry;
public function __construct()
{
$this->registry = Registry::getInstance();
}
public function foo($id)
{
return $this->doSomethingWithResults(
$registry->get('MyActiveRecord')->findById($id)
);
}
}
To get this working you have to have the concrete Registry. It's hardcoded, and it's a Singleton. The latter means to prevent any side-effects from a previous test. It has to be reset for each test you will run on MyTestSubject. You could add a Registry::reset() method and call that in setup(), but adding a method just for being able to test seems ugly. Let's assume you need this method anyway, so you end up with
public function setup()
{
Registry::reset();
$this->testSubject = new MyTestSubject;
}
Now you still don't have the 'MyActiveRecord' object it is supposed to return in foo. Because you like Registry, your MyActiveRecord actually looks like this
class MyActiveRecord
{
protected $db;
public function __construct()
{
$registry = Registry::getInstance();
$this->db = $registry->get('db');
}
public function findById($id) { … }
}
There is another call to Registry in the constructor of MyActiveRecord. You test has to make sure it contains something, otherwise the test will fail. Of course, our database class is a Singleton as well and needs to be reset between tests. Doh!
public function setup()
{
Registry::reset();
Db::reset();
Registry::set('db', Db::getInstance('host', 'user', 'pass', 'db'));
Registry::set('MyActiveRecord', new MyActiveRecord);
$this->testSubject = new MyTestSubject;
}
So with those finally set up, you can do your test
public function testFooDoesSomethingToQueryResults()
{
$this->assertSame('expectedResult', $this->testSubject->findById(1));
}
and realize you have yet another dependency: your physical test database wasn't setup yet. While you were setting up the test database and filled it with data, your boss came along and told you that you are going SOA now and all these database calls have to be replaced with Web service calls.
There is a new class MyWebService for that, and you have to make MyActiveRecord use that instead. Great, just what you needed. Now you have to change all the tests that use the database. Dammit, you think. All that crap just to make sure that doSomethingWithResults works as expected? MyTestSubject doesn't really care where the data comes from.
Introducing mocks
The good news is, you can indeed replace all the dependencies by stubbing or mock them. A test double will pretend to be the real thing.
$mock = $this->getMock('MyWebservice');
$mock->expects($this->once())
->method('findById')
->with($this->equalTo(1))
->will($this->returnValue('Expected Unprocessed Data'));
This will create a double for a Web service that expects to be called once during the test with the first argument to method findById being 1. It will return predefined data.
After you put that in a method in your TestCase, your setup becomes
public function setup()
{
Registry::reset();
Registry::set('MyWebservice', $this->getWebserviceMock());
$this->testSubject = new MyTestSubject;
}
Great. You no longer have to bother about setting up a real environment now. Well, except for the Registry. How about mocking that too. But how to do that. It's hardcoded so there is no way to replace at test runtime. Crap!
But wait a second, didn't we just say MyTestClass doesn't care where the data comes from? Yes, it just cares that it can call the findById method. You hopefully think now: why is the Registry in there at all? And right you are. Let's change the whole thing to
class MyTestSubject
{
protected $finder;
public function __construct(Finder $finder)
{
$this->finder = $finder;
}
public function foo($id)
{
return $this->doSomethingWithResults(
$this->finder->findById($id)
);
}
}
Byebye Registry. We are now injecting the dependency MyWebSe… err… Finder?! Yeah. We just care about the method findById, so we are using an interface now
interface Finder
{
public function findById($id);
}
Don't forget to change the mock accordingly
$mock = $this->getMock('Finder');
$mock->expects($this->once())
->method('findById')
->with($this->equalTo(1))
->will($this->returnValue('Expected Unprocessed Data'));
and setup() becomes
public function setup()
{
$this->testSubject = new MyTestSubject($this->getFinderMock());
}
Voila! Nice and easy and. We can concentrate on testing MyTestClass now.
While you were doing that, your boss called again and said he wants you to switch back to a database because SOA is really just a buzzword used by overpriced consultants to make you feel enterprisey. This time you don't worry though, because you don't have to change your tests again. They no longer depend on the environment.
Of course, you still you have to make sure that both MyWebservice and MyActiveRecord implement the Finder interface for your actual code, but since we assumed them to already have these methods, it's just a matter of slapping implements Finder on the class.
And that's it. Hope that helped.
Additional Resources:
You can find additional information about other drawbacks when testing Singletons and dealing with global state in
Testing Code That Uses Singletons
This should be of most interest, because it is by the author of PHPUnit and explains the difficulties with actual examples in PHPUnit.
Also of interest are:
TotT: Using Dependency Injection to Avoid Singletons
Singletons are Pathological Liars
Flaw: Brittle Global State & Singletons
Singletons (in all OOP languages, not just PHP) make a particular kind of debugging called unit testing difficult for the same reason that global variables do. They introduce global state into a program, meaning that you can't test any modules of your software that depend on the singleton in isolation. Unit testing should include only the code under test (and its superclasses).
Singletons are essentially global state, and while having global state can make sense in certain circumstances, it should be avoided unless it's necessary.
When finishing a PHP test, you can flush singleton instance like this:
protected function tearDown()
{
$reflection = new ReflectionClass('MySingleton');
$property = $reflection->getProperty("_instance");
$property->setAccessible(true);
$property->setValue(null);
}
I'm a big fan of OOP in php, but i feel like defining class methods gets disorganized so fast. I have a pretty good background in OOP in C++, and i am pretty comfortable with how it is handled there, and am curious if there are ways to do it similarly in php.
To be more specific, here is what i mean. I like how in C++ you can define a class header (myclass.h) and then define the actual details of the functions in the implementation file (myclass.cc). Ive found that this can easily be replicated using interfaces in php, but i havent found a good solution for the following:
I like to organize my code in C++ in different files based on how they are accessed, so for example, public methods that can be called outside of the class would be in 1 place, and private methods would be organized somewhere else - this is personal preference.
Ive tried to define class methods in php like:
private function MyPHPClass::myFunction(){ }
when the definition isnt directly inside the class block( { } ), but i havent had any success doing this.
Ive been through all of the pages on php.net, but couldnt find anything like this. Im assuming that there is no support for something like this, but thought i would ask anyway.
thanks
You can't do this. The class declarations are Java-like.
You have to put everything in one file or, at minimum, have some helper classes -- be they only static methods to which you forward or calls or with you deferring some implementation to encapsulated objects. You can also use the __call and __callstatic magic methods to reduce the size of your stubs, but that only works for public methods and I would recommend that you avoid magic methods.
EDI2: As RobertPitt pointed in a comment, you should consider alternative strategies that divide the functionality between several classes. It has the added advantage that it can make your code more decoupled. If you need, you can still unify the functionality of the several classes behind a façade.
EDIT: By using magic methods, I mean something like this:
class MyClassHelper {
public function method_one(array $args) {
...
}
public function method_two(array $args) {
...
}
}
class MyClass {
/**
* #var MyClassHelper
*/
private $helper;
private static $ALLOWED_METHODS = array("method_one" => NULL,
"method_two" => NULL);
public function __call($name, $arguments) {
$name = strtolower($name);
if (array_key_exists($name, self::$ALLOWED_METHODS) {
$helper->$name($arguments);
}
else
throw new Exception(...);
}
}
I should recommend that you avoid this, there are many pitfalls to this (handling of references, no code completion).
Im not really a C++ / C# Programmer but interfaces in php i can give you an exampe to see if this helps.
Interface
interface IDatabase
{
public function connect($dns = '');
public function disconnect($flushCache = false); //Do not use braces, Code free in interfaces
}
Database abstract base class
abstract class Database
{
//Some driver logic here to laod mysql,oracle etc
}
MySql Driver
class DBDriver_MySQl extends Database implements IDatabase
{
public function connect($dns = '')
{
//Connection logic.
}
public function disconnect($flushDns)
{
//Disconnect Login
}
}
Hope this is what your looking for.