Assert that a function gets called - php

When testing with phpunit, I want to assert a function call:
Given a Class:
Class TimeWrapper {
public function time() {
return time();
}
}
And Its unittest:
Class TimeWrapperTest extends PHPUnit_FrameworkTestCase {
public function testTime() {
//Pseudocode as example of a possible solution:
$this->assertCallsFunction("time");
}
}
I am specifically looking for a way to test calling of global functions.
FWIW: with rspec, I use Message Expectations. I am looking to achieve something similar, or exactly similar in PHPUnit.

If the goal is to verify that TimeWrapper calls the built-in PHP function time, you'll need to use the runkit extension. This will allow you to replace the built-in function with your own version that will record the call. You'll need to enable the runkit.internal_override setting in php.ini to allow you to rename internal functions.
class TimeWrapperTest extends PHPUnit_Framework_TestCase {
static $calledTime;
function setUp() {
self::$calledTime = false;
}
function testTimeGetsCalled() {
$fixture = new TimeWrapper;
try {
runkit_function_rename('time', 'old_time');
runkit_function_rename('new_time', 'time');
$time = $fixture->time();
self::assertTrue('Called time()', $calledTime);
}
catch (Exception $e) {
// PHP lacks finally, but must make sure to revert time() for other test
}
runkit_function_rename('time', 'new_time');
runkit_function_rename('old_time', 'time');
if ($e) throw $e;
}
}
function new_time() {
TimeWrapperTest::$calledTime = true;
return old_time();
}
If you cannot use the extension or just want to avoid that kind of trickery, you could modify TimeWrapper to allow you to override the function that gets called at runtime.
class TimeWrapper {
private $function;
public function __construct($function = 'time') {
$this->function = $function;
}
public function time() {
return call_user_func($this->function);
}
}
Use the test case above without the calls to runkit_function_rename and pass new_time to the TimeWrapper constructor. The downside here is that you'll pay a (probably tiny) performance penalty in production on each call to TimeWrapper::time.

Not sure if there's something already done for that purpose.
But if you're trying to implement it yourself, you could take a look at xdebug code coverage

Related

Monkey patching in PHP 7

This four year old question uses third party libraries which I am a little dubious about.
For testing purposes only, I want to redefine a static method of one of my classes. Take the following example:
class Driver {
public static function getVersion() : string
{
// Retrieves a version from a system executable
return some_system_call();
}
}
class Module {
public function methodToTest()
{
if (Driver::getVersion() === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
I need for the Driver::getVersion to return different version strings. I would usually mock the class, but since this is neither injected nor an instance, it's not going to work.
I could change the source, adding in methods and property testing, so that the classes being tested would never need to call Driver, but, in my opinion, refactoring the code just to make tests "work" is not a solution.
I'm thinking along the lines of creating another Driver class and somehow loading it in place of the original.
How can I do this?
You might wanna use smth like:
class Module
{
private $version;
public function __construct($version){
$this->version = $version;
}
public function methodToTest()
{
if ($this->version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
or another option would be injecting not version but a provider for that (if you know you will have some bit of complicated logic for versioning control -- so you can split the logic between Module and Provider as appropriate):
class Module
{
private $versionProvider;
public function __construct($provider){
$this->versionProvdier = $provider;
}
public function methodToTest()
{
if ($this->versionProvider->getVersion() === '4.0.0') {
// it could be even $this->versionProvider->newFeaturesAreSupported()
} else {
// some other stuff
}
}
}
and still another could be implementing some proxy class like
class Module
{
public function methodToTest()
{
$myMonostateProxy = new MyMonostateProxy();
$version = $myMonostateProxy->getVersion();
if ($version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
so you can mock your monostate separately (probably via reflectioning on privtates or via its public interface, anyway don't forget to tearDown it). Real implementation of it would just call that uncontrollable Driver::getVersion().
I think first two options are cleaner but require some efforts for creation (as you need some injection to perform).
Third has that hidden dependecy and is somewhat tricky in testing and thus not quite clean and needs more efforts to maintaine but hides all that choice stuff inside itself making regular usage easier.
class Driver {
private static $testVersion;
public static function setTestVersion(string $testVersion = null)
{
static::$testVersion = $testVersion;
}
public static function getVersion() : string
{
if (static::$testVersion !== null) {
return static::$testVersion;
}
// Retrieves a version from a system executable
return some_system_call();
}
}
You could register a class loader that is somehow made aware of the testing and loads a modified Driver class from a different location.

Trigger an event when a function is called in a class

Is is possible in PHP to trigger an event whenever a function in a class is called, without adding it to every function in the class?
Example:
<?php
class A {
function xxx() {
//this function will be called everytime I call another function in this class
}
public static function b() {
return 'Hello Stackoverflow!';
}
public static function c() {
//I also want this function to trigger the event!
}
}
echo A::b();
?>
AFAIK there are no native language constructs for this. If you need it for debugging purposes I would advice you to have deeper look into the xdebug extension especially function traces (awesome! :)
Another idea would be to implement __call() in your class and wrap all public methods. But this requires to change the code and has other side effects:
(simplified example)
class Test {
protected $listeners;
public function __construct() {
$this->listeners = array();
}
private function a() {
echo 'something';
}
private function b() {
echo 'something else';
}
public function __call($fname, $args) {
call_user_func_array(array($this, $fname), $args);
foreach($this->listeners as $listener) {
$listener->notify('fname was called');
}
}
public function addListener(Listener $listener) {
$this->listeners[]= $listener;
}
}
.
class Listener {
public function notify($message) {
echo $message;
}
}
Example:
$t = new Test();
$l = new Listener();
$t->addListener($l);
$t->a();
This is a classic task for aspect oriented programming (AOP). PHP has no native support for AOP, however, there are some frameworks that make AOP in PHP possible. One of these is the GO! AOP PHP framework. You can also implement AOP using runkit.
You need for PHP SplObserver: From PHP Doc
This is a classic task for dependency injection and lazy initialization! The dependency is the MySQL connection. As it first needs to be available when the first query is executed, it need not be initialized at "startup", but only then. This is called lazy initialization, and its implementation is extremly simple:
class DbStuff {
private $__conn = NULL;
protected function _getConn() {
if ( is_null( $this->__conn ) {
$this->__conn = ... ; // init (MySQL) DB connection here
// throw on errors!
}
return $this->__conn;
}
public function someQuery($arg1, $arg2) {
$conn = $this->_getConn();
// MySQL query here:
...
}
}
All "refactoring" required is calling $this->_getConn() in every query method.
Aspect oriented programming is not the instrument to solve this, because the DB connection is an innate dependency of the query, and not an aspect of it. Automatic logging of all queries executed were an aspect.
A trigger built around PHP's __call() isn't a good choice either; aside from knocking out modern IDE's inspections - which are great to see quickly whether a module is fine - it would unnecessarily complicate tests: a protected $this->_getWhatever() can easily be overwritten in a test facade object - derived from the class to test - to return a mock object or whatever. With __call(), more code is needed for the same purpose, which induces the risk of errors in code which is only there for testing (and should be absolutely free of errors)

If function called internally

Is there any way of identifying if a function was called from within the same class in PHP?
Besides using something like debug_backtrace ?
Update:
This is what I'm currently doing:
class Alex {
function __construct()
{
$this->internal = false;
}
function a()
{
$this->internal = true;
$this->b();
}
function b()
{
if($this->internal)
// ...
else
// ...
}
}
I 'm not sure why you would want to do that; this kind of requirement is suspicious, and usually for good reason.
That said, you can make a protected clone of the public function with an additional parameter that tells you if the caller was internal or external, and make the public version defer to the protected implementation.
class Before
{
public foo() { /* code here */ }
}
class After
{
public foo() { $this->fooCore(false); }
protected fooCore($calledFromInside = true) { /* code here */ }
// At this point you should make sure that you never call $this->foo()
// directly from inside class After
}
Not that I'm aware of. I solved a similar problem by having an extra optional parameter that passed a value when called from inside the class.
HTH :)
It is possible to debug PHP for instance in NetBeans like you can see to debugging in NetBeans. Also, you can find useful tools onsseful PHP tools, like Webgrind or Xdebug.

What is causing this strange PHP fatal error?

I have a generic Logger class that looks like this:
class Logger {
...
public function add($userId, $siteId, $logTypeId, $message) {
$Log = LogMapper::create();
$Log->setUserId($userId);
$Log->setSiteId($siteId);
$Log->setLogTypeId($logTypeId);
$Log->setMessage($message);
$Log->save();
...
}
...
}
And the Log class:
class Log {
public function setUserId($userId) {
if ($this->userId !== $userId) {
$this->userId = $userId;
}
return $this;
}
public function getUserId() {
return $this->userId;
}
public function setSiteId($siteId) {
if ($this->siteId !== $siteId) {
$this->siteId = $siteId;
}
return $this;
}
public function getSiteId() {
return $this->siteId;
}
...
}
As well as the LogMapper class:
class LogMapper extends DataMapper {
...
public static function create($row = false) {
return new Log($row);
}
public static function getById($id) {
...
}
}
As you can see, I have two other classes, LogMapper and Log, which Logger uses to write records to a database.
I also have a mechanism that emails me when a fatal error occurs. I received the following in about a dozen emails:
Call to undefined method Log::setUserId()
My application uses autoloading, and I first thought that may be the problem, but clearly the Logger class is being loaded, and so autoloading has not broken. The path for the Log class is correct in the autoloader...and clearly the Log class has been loaded--otherwise a "Class 'Log' not found" error would have been thrown.
Any ideas what may be causing this error? I do use eAccelerator on the release.
Long shot, but do you have PEAR's Log class installed? This is something I ran across a while back. I tried to make a 'Log' class but it was colliding with PEAR's.
Since you didn't post your Log class here, the best guess anyone can probably make is that you forgot to write a setUserId() method in that class.
I can also take a guess that eAccelerator might have your class cached. It's possible you need to restart your web server for changes to take effect, or utilize some other method of clearing the cache.
Maybe reflection can tell you a bit more about the actual class Log used by your code.
public function add($userId, $siteId, $logTypeId, $message) {
$Log = LogMapper::create();
if ( !method_exists($Log, 'setUserId') ) {
$ro = new ReflectionObject($Log);
echo 'class defined in ', $ro->getFilename(), ' # ', $ro->getStartLine(), "\n";
foreach($ro->getMethods() as $rm) {
echo ' method ', $rm->name, " \n";
}
die('----');
}
$Log->setUserId($userId);
$Log->setSiteId($siteId);
$Log->setLogTypeId($logTypeId);
$Log->setMessage($message);
$Log->save();
}
It's hard to say, we can't see the Log class nor the LogMapper.
You sure the Log class has setUserId() method?
Also why do you name the variable with uppercase? $Log?
Just a quick guess:
Use:
public function __set($name,$value)
{
$this->{preg_replace('^set','',$name)} = $value;
}
This would be a real mapping, I guess.
//Call me stupid, but I still can not find the setUserId() method?
//Did you leave it out, while copy&pasting the code? OUTDATED
try this
if ($this->userId !== $userIdd)
make the var names diffrent, i had such a weird problem one day.

Redefine Class Methods or Class

Is there any way to redefine a class or some of its methods without using typical inheritance? For example:
class third_party_library {
function buggy_function() {
return 'bad result';
}
function other_functions(){
return 'blah';
}
}
What can I do to replace buggy_function()? Obviously this is what I would like to do
class third_party_library redefines third_party_library{
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
This is my exact dilemma: I updated a third party library that breaks my code. I don't want to modify the library directly, as future updates could break the code again. I'm looking for a seamless way to replace the class method.
I've found this library that says it can do it, but I'm wary as it's 4 years old.
EDIT:
I should have clarified that I cannot rename the class from third_party_library to magical_third_party_library or anything else because of framework limitations.
For my purposes, would it be possible to just add a function to the class? I think you can do this in C# with something called a "partial class."
It's called monkey patching. But, PHP doesn't have native support for it.
Though, as others have also pointed out, the runkit library is available for adding support to the language and is the successor to classkit. And, though it seemed to have been abandoned by its creator (having stated that it wasn't compatible with PHP 5.2 and later), the project does now appear to have a new home and maintainer.
I still can't say I'm a fan of its approach. Making modifications by evaluating strings of code has always seemed to me to be potentially hazardous and difficult to debug.
Still, runkit_method_redefine appears to be what you're looking for, and an example of its use can be found in /tests/runkit_method_redefine.phpt in the repository:
runkit_method_redefine('third_party_library', 'buggy_function', '',
'return \'good result\''
);
runkit seems like a good solution but its not enabled by default and parts of it are still experimental. So I hacked together a small class which replaces function definitions in a class file. Example usage:
class Patch {
private $_code;
public function __construct($include_file = null) {
if ( $include_file ) {
$this->includeCode($include_file);
}
}
public function setCode($code) {
$this->_code = $code;
}
public function includeCode($path) {
$fp = fopen($path,'r');
$contents = fread($fp, filesize($path));
$contents = str_replace('<?php','',$contents);
$contents = str_replace('?>','',$contents);
fclose($fp);
$this->setCode($contents);
}
function redefineFunction($new_function) {
preg_match('/function (.+)\(/', $new_function, $aryMatches);
$func_name = trim($aryMatches[1]);
if ( preg_match('/((private|protected|public) function '.$func_name.'[\w\W\n]+?)(private|protected|public)/s', $this->_code, $aryMatches) ) {
$search_code = $aryMatches[1];
$new_code = str_replace($search_code, $new_function."\n\n", $this->_code);
$this->setCode($new_code);
return true;
} else {
return false;
}
}
function getCode() {
return $this->_code;
}
}
Then include the class to be modified and redefine its methods:
$objPatch = new Patch('path_to_class_file.php');
$objPatch->redefineFunction("
protected function foo(\$arg1, \$arg2)
{
return \$arg1+\$arg2;
}");
Then eval the new code:
eval($objPatch->getCode());
A little crude but it works!
For people that are still looking for this answer.
You should use extends in combination with namespaces.
like this:
namespace MyCustomName;
class third_party_library extends \third_party_library {
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
Then to use it do like this:
use MyCustomName\third_party_library;
$test = new third_party_library();
$test->buggy_function();
//or static.
third_party_library::other_functions();
For the sake of completeness - monkey patching is available in PHP through runkit. For details, see runkit_method_redefine().
How about wrapping it in another class like
class Wrapper {
private $third_party_library;
function __construct() { $this->third_party_library = new Third_party_library(); }
function __call($method, $args) {
return call_user_func_array(array($this->third_party_library, $method), $args);
}
}
Yes, it's called extend:
<?php
class sd_third_party_library extends third_party_library
{
function buggy_function() {
return 'good result';
}
function other_functions(){
return 'blah';
}
}
I prefixed with "sd". ;-)
Keep in mind that when you extend a class to override methods, the method's signature has to match the original. So for example if the original said buggy_function($foo, $bar), it has to match the parameters in the class extending it.
PHP is pretty verbose about it.
Zend Studio and PDT (eclipse based ide) have some built in refractoring tools. But there are no built in methods to do this.
Also you wouldn't want to have bad code in your system at all. Since it could be called upon by mistake.
I've modified the code from the answer by #JPhilly and made it possible to rename a the patched class to avoid errors.
Also, I've changed the regex that identifies the about-to-be-replaced function to fit cases where the replaced function doesn't have any class access modifiers in front of its name
Hope it helps.
class Patch {
private $_code;
public function __construct($include_file = null) {
if ( $include_file ) {
$this->includeCode($include_file);
}
}
public function setCode($code) {
$this->_code = $code;
}
public function includeCode($path) {
$fp = fopen($path,'r');
$contents = fread($fp, filesize($path));
$contents = str_replace('<?php','',$contents);
$contents = str_replace('?>','',$contents);
fclose($fp);
$this->setCode($contents);
}
function redefineFunction($new_function) {
preg_match('/function ([^\(]*)\(/', $new_function, $aryMatches);
$func_name = trim($aryMatches[1]);
// capture the function with its body and replace it with the new function
if ( preg_match('/((private|protected|public)?\s?function ' . $func_name .'[\w\W\n]+?)(private|protected|public|function|class)/s', $this->_code, $aryMatches) ) {
$search_code = $aryMatches[1];
$new_code = str_replace($search_code, $new_function."\n\n", $this->_code);
$this->setCode($new_code);
return true;
} else {
return false;
}
}
function renameClass($old_name, $new_name) {
$new_code = str_replace("class $old_name ", "class $new_name ", $this->_code);
$this->setCode($new_code);
}
function getCode() {
return $this->_code;
}
}
This is how I've used it to patch a Wordpress plugin:
$objPatch = new Patch(ABSPATH . 'wp-content/plugins/a-plugin/code.php');
$objPatch->renameClass("Patched_AClass", "Patched_Patched_AClass"); // just to avoid class redefinition
$objPatch->redefineFunction("
function default_initialize() {
echo 'my patched function';
}");
eval($objPatch->getCode());
$result = new Patched_AClass();
If the library is explicitly creating the bad class and not using a locater or dependency system you are out of luck. There is no way to override a method on another class unless you subclass.
The solution might be to create a patch file that fixes the library, so you can upgrade the library and re-apply the patch to fix that specific method.
You might be able to do this with runkit. http://php.net/runkit
You can make a copy of the library class, with everything the same except the class name. Then override that renamed class.
It's not perfect, but it does improve the visibility of the extending class's changes. If you fetch the library with something like Composer, you'll have to commit the copy to source control and update it when you update the library.
In my case it was an old version of https://github.com/bshaffer/oauth2-server-php. I modified the library's autoloader to fetch my class file instead. My class file took on the original name and extended a copied version of one of the files.
Since you always have access to the base code in PHP, redefine the main class functions you want to override as follows, this should leave your interfaces intact:
class third_party_library {
public static $buggy_function;
public static $ranOnce=false;
public function __construct(){
if(!self::$ranOnce){
self::$buggy_function = function(){ return 'bad result'; };
self::$ranOnce=true;
}
.
.
.
}
function buggy_function() {
return self::$buggy_function();
}
}
You may for some reason use a private variable but then you will only be able to access the function by extending the class or logic inside the class. Similarly it's possible you'd want to have different objects of the same class have different functions. If so, do't use static, but usually you want it to be static so you don't duplicate the memory use for each object made. The 'ranOnce' code just makes sure you only need to initialize it once for the class, not for every $myObject = new third_party_library()
Now, later on in your code or another class - whenever the logic hits a point where you need to override the function - simply do as follows:
$backup['buggy_function'] = third_party_library::$buggy_function;
third_party_library::$buggy_function = function(){
//do stuff
return $great_calculation;
}
.
.
. //do other stuff that needs the override
. //when finished, restore the original function
.
third_party_library::$buggy_function=$backup['buggy_function'];
As a side note, if you do all your class functions this way and use a string-based key/value store like public static $functions['function_name'] = function(...){...}; this can be useful for reflection. Not as much in PHP as other languages though because you can already grab the class and function names, but you can save some processing and future users of your class can use overrides in PHP. It is however, one extra level of indirection, so I would avoid using it on primitive classes wherever possible.
There's alway extending the class with a new, proper, method and calling that class instead of the buggy one.
class my_better_class Extends some_buggy_class {
function non_buggy_function() {
return 'good result';
}
}
(Sorry for the crappy formatting)

Categories