I have a file with a single include like this one:
file.php
include(dirname(__FILE__)."/anotherfile.php");
class file {
function myfunction() {
$class = new anotherfile();
$class->functionfromanotherfile();
}
function myfunctionToBeTested() {
// do things here
}
}
I'm using travis-ci to run my tests with phpunit, and the problem is that I dont commit the "anotherfile.php" (have the logic and credentials to connect to the database), which makes it not work when I try to include this file in my test script.
tests.php
class tests extends PHPUnit_Framework_TestCase {
public function test_function() {
include(dirname(__FILE__)."/file.php");
$class = new file();
}
}
I could make another file with only the function I need to test and go with it but maybe you guys can come up with other awnser.
Use some kind of dependency injection:
Modify the class you want to test so that it accepts the credentials as parameters of the constructor.
In the test, create fake -or real- credentials and pass them to the constructor of your class.
Related
I'm trying to write a class in PHP that acts as a wrapper for a collection of command line tools to make them easier to use from PHP.
I have a single class (MyClass) in a file myclass.php.
I have code that checks to see if the required tools are installed and then sets a constant (TOOLS_AVAILABLE) to either true or false. Although it's not a lot of code, I only want it to run the first time somebody tries to instantiate my class or use any of its static functions. What's the best practice for handling this?
I only want it to run the first time somebody tries to instantiate my class or use any of its static functions.
Well, the best answer is not to have any static methods. Then you can stick the code in a constructor method as per the answer by #treegarden.
If you must have static methods, then you'll need a static flag within the class to indicate when you've called the 'run once' code, so you can avoid running it again. And then call it explicitly from each of your static methods and the constructor. Something like this:
<?php
class myClass {
private static $hasRunOnce = false;
private static runMeOnce()
{
if (!self::$hasRunOnce) {
self::$hasRunOnce = true;
//put your 'run once' code here...
}
}
public static oneOfYourStaticMethods()
{
self::runMeOnce();
//put your static method code here...
//this would be the same for each of your static methods and your constructor.
}
}
Hope that helps.
You need to create a __construct function in your class and put whatever code you want to execute on instantiation in there:
class MyClass {
function __construct(/* arguments */) {
/* your code here */
}
}
The code will get executed only once when someone instantiates the class.
I'm a newbie in php but I'll try to get straight to the point.
I have a class called ConnectionManager
class ConnectionManager
{
function ConnectToDB()
{
//PDO connection code
}
}
and in my other manager InstitutManager I am using require_once($filename) to get access to my ConnectionManager functions
require_once('../manager/ConnectionManager.php');
class InstitutManager
{
protected $connInstance;
function _construct()
{
$this->connInstance = new ConnectionManager;
}
function getInstituts()
{
$conn = $connManager->ConnectToDb();
//retrieve instituts
}
}
The question is : Should I be using extends ConnectionManager in my InstitutManager instead of require_once? Why should I use one more than the other?
Thanks
Edit : Changed code for InstitutManager class
Would this be ok like this? Or should I pass a pass a parameter with my connection already instanciated in function _construct($conn)?
Your include_once reads in a source file, which in this case has a class definition for ConnectionManager in it. Your extends sets up class InstitutManager as inheriting class ConnectionManager, i.e. InstitutManager gets everything in ConnectionManager and can then define its own modifications to that basic structure. There isn't really any relationship at all between the two operations, and your $connManager = new ConnectionManager operations are nonsensical.
require_once 'file'.php' just means that the PHP interpreter will take the contents of a file called file.php and dump it right there in the spot where the include was called. Kind of like what would happen if you would select everything in a Word file, click copy and paste it at the top of another Word file.
In your case you need to include the file, or else it will not know where to find the ConnectionManager class.
I have got a PHP file in which there are some functions (not included in a class). I am using PHPUnit to testing. When I try to generate in a simply way a test file from a file containing functions, the log says:
Could not find class...
Is there any possibility to test functions which are not methods?
Yes, you can with something like this:
includes/functions.php
function my_function() {
return true;
}
tests/MyFunctionTest.php
require_once '../includes/functions.php';
class MyFunctionTest extends PHPUnit_Framework_TestCase
{
public function testReturnValue()
{
$return_value = my_function();
$this->assertTrue($return_value);
}
}
So as long as your function is within scope you can call it from your test method just like any other PHP function in any other PHP framework or project.
If I'm right then PhpUnit works only with classes hence methods. So just convert them into the methods for testing purpose. Shouldn't be hard.
Is it possible to rename a class method in PHP 5.2 during run time? Is it possible to do that using Reflection?
Given:
class Test
{
public function myMethod()
{
echo 'in my method';
}
}
I want to be able to rename myMethod() to oldMethod() so that later I do this:
$test = new Test();
$test->oldMethod(); // in my method
$test->myMethod(); // run time error: method does not exist
From comment below question:
because I would like to install call event handlers on classes, without the classes knowing about it, so I would know that the user is calling a method from a class before that method is actually called.
Solution: use a Decorator.
class EventDecorator
{
protected $_instance;
public function __construct($instance)
{
$this->_instance = $instance;
}
public function __get($prop)
{
printf('Getting %s in %s', $prop, get_class($this->_instance));
return $this->_instance->$prop;
}
public function __set($prop, $val)
{
printf('Setting %s with %s in %s',
$prop, $val, get_class($this->_instance));
return $this->_instance->$prop = $val;
}
public function __call($method, $args)
{
printf('Calling %s with %s in %s',
$method, var_export($args, TRUE), get_class($this->_instance));
return call_user_func_array(array($this->_instance, $method), $args);
}
}
Then you can wrap any class into it:
class Foo
{
public $prop;
public function doSomething() {}
}
$foo = new EventDecorator(new Foo);
$foo->prop = 'bar'; // Setting prop with bar in Foo
$foo->prop; // Getting prop in Foo
$foo->doSomething('something');
// Calling doSomething with array (0 => 'something',) in Foo
This can be fleshed out to provide pre and post hooks. You could also make the Decorator use the Subject/Observer Pattern and fire events to whatever other object registered to the decorator. The above approach is more maintainable and understandable than monkeypatching random methods with runkit.
Additional notes:
You might be interested in the Symfonys EventDispatcher component.
If you are after Aspect Oriented programming, read http://sebastian-bergmann.de/archives/573-Current-State-of-AOP-for-PHP.html - it's from 2006 but there is not much changed in that field since then.
If you are after horizontal reuse, you will like PHP's Trait functionality that is supposed to be in one of the next few versions.
Using Runkits runkit_method_rename you can do this.
For all of you who are asking why you would need this, I have an idea that is very similar. My idea is to attempt to rename an entire PHP Class on the fly. It would be used, in my case, for an IRC Chat bot that I would have load and instantiate plugins on the fly, so that I would no need to reboot the bot and uptime would be very long. This would include renaming of pre-loaded classes of the same name as the class I would be attempting to load, so that there would be no conflict and it would run properly.
For example:
I have $bot running on irc.example.com
I have the plugin test.php installed and working, now when this is loaded into memory I can alter the file test.php without any change to $bot
So I update test.php
Now I want to cause it to load into $bot, but $bot already has a test load in it, and it would conflict if I attempted to include test.php again
so instead, we run a rename function to rename class test to class test[sha1 of a counter]
and then we include 'test.php'
$bot->test = new test();
and there we have it, an updated test plugin installed and loaded into memory on $bot with no reboot.
This is all theory, but it's something to think about before instantly flaming someone for their ideas with the "Why would you even need this" attitude.
I mean, let's be honest here. What are the odds that you're a super genius who knows everything there is to know about programming and would know what everyone would or wouldn't need?
I am stumped right now. In my last post about this question the answer was to use a singleton to make sure an object is only initiated 1 time but I am having the opposite problem.
If I have a file called index.php and then I include these files into it, class1.php, class2.php, class3.php, class4.php.
In index.php I will have,
<?PHP
$session = new Session();
require_once '/includes/class1php';
require_once '/includes/class2.php';
require_once '/includes/class3.php';
require_once '/includes/class4.php';
?>
then in all 4 of the test files I will try to access a method called get() from the session class, assume the session class file is already included into the index.php page as well.
Now if I try to use...
$testvar = $session->get($var1);
in any of the test class files I will get this error
Fatal error: Call to a member function get() on a non-object
the only way the code works without an error is if I use
$session = new Session();
in every file.
How can I fix/avoid having to initaite the class in every file when it is already initated in the index.php file?
the goal is to let me initiate a class in 1 file like index.php and then include the class files into that page, the catch is most of the classes use methods from other classes so would be nice if I didn't have to initiate every class in every file
Without seeing the code it's hard to tell, but I think I can make some assumptions. correct me if I'm wrong:
EDIT: So post your source so we can stop speculating
1) The files you are including are class files. in other words, they contain something like:
class a
{
function a(){}
function b()
{
}
}
2) You aren't trying to execute code in the class files, at load time, but at some later time by instantiating them
i.e.
require("class.a.php");
$myA = new a();
$a->b();
If you are trying to reference your session variable inside those classes, then you have a scope issue. A variable declared outside a class definition can't be used inside the class, unless it is declared as a global var inside the class.
class a
{
function a(){}
function willFail()
{
$session->doSomething(); //fails
}
function b()
{
global $session;
$session->doSomething(); //succeeds
}
}
Even then, you probably don't want to do that, but instead you should pass in your session as a variable if the class needs access to it:
class a
{
function a(){}
function b($session)
{
$session->doSomething(); // yay!
}
}
You could have a base class they all all extend from
Example
class test1 extends Base {
public function doSomething() {
$this->session->get('something');
}
}
class Base {
protected session;
public function __construct() {
$this->session = new Session();
}
}
You're kind of thinking about it backwards. Any file that will use the session object will need to include the file containing that class definition. The alternative is to use __autoload to pull the class in:
function __autoload($classname)
{
if ($classname == 'Session')
{
include_once 'Session.php';
}
}
EDIT : you'll need to put the file containing that autoload into every file that will use it.