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.
Related
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.
I want to call a static method from a variabe class in PHP. As pointed out several times on SO and because it is general practice, the following works as expected:
class Foo {
public function compile($strClass) {
$strClass::find(); // this works
}
}
Nonetheless I have to call different find methods from $strClass from different methods of a class Foo. That is, why I want to store $strClass in $this->strClass. Unfortunately, this doesn't work:
class Foo {
protected $strClass;
public function __construct($strClass)
{
$this->strClass = $strClass;
}
public function compile($strClass) {
$this->strClass::find(); // this does not work
}
}
Any idea or hint on how to solve that issue?
Update:
As pointed out in the comments, it might be a solution to use call_user_func like this:
call_user_func(array($this->strClass, 'find'), $strParam);
Anyhow, this makes code completion in PHPstorm impossible. Any hints on that? Maybe using code annotation?
You can change your compile method to this:
public function compile($strClass) {
call_user_func(array($this->strClass, 'find'));
}
This class design is flawed. I would try to get rid of the static methods completely, but here is a solution that exploits the fact that you can call static methods on objects:
class Foo {
protected $strClass;
public function __construct($strClass)
{
$this->strClass = new $strClass;
}
public function compile($strClass) {
$this->strClass::find();
}
}
UPDATE: nevermind, this is a syntax error in all current PHP versions, you actually have to do it like this:
$strClass = $this->strClass;
$strClass::find();
And this works with your original code as well, where $this->strClass is a string.
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)
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
I'm currently working on an own PHP-MVC-Framework (for experience purposes only).
My question: Is it possible to call a defined function or method, every time a class-method
has been called?
For example:
public function view($id) {
//Code ...
$this->view->render(__FUNCTION__);
}
What I want is:
public function view($id) {
//Code ...
//render-method is called automatically with functionname as parameter
}
I tried different methods ... but without success.
Would be great if someone could help me out with this.
Cheers,
Chris
You can use Magic Methods do achieve this behavior:
public function __call($func, $args) {
if(!method_exists($this, $func)) {
return;
}
// do some coding here
call_user_func_array($func,$args);
// do some coding there
}
private function view($arg1, $arg2) {
// and here
}
Remember: view function must be private/protected.
$obj->view("asdasd", "asdsad");
Should do ::__call(), then ::view() method
You could create a function as a liaison using PHP's ability to use variable values for execution purposes. for example:
function call($func,$param)
{
$this->$func($param);
$this->render($func);
}
$myObj->call('view',$id);
You can use a wrapper method. Call this method and pass everything else as a parameters.