I have some classes set up as this, where -> means 'extends':
DBObject -> Article -> Activity
DBObject contains a generic __construct to easily load my objects from the database (hence DataBaseObject).
Article overrides this __construct, for some specialized constructor behaviour.
Activity does not implement any __construct, as it can be handled by its superclass' __construct (Article::__construct).
However, for some reason, if I call
$activity = new Activity($args);
It ends up in DBObject::__construct, and passes Article's one all together. I always thought that calls on a subclass were supposed to travel up the subclass line one class at a time. Am I mistaken in thinking that?
EDIT: Here's a code snippet: http://pastebin.com/SXpSNVMm. I removed all non-necessary code. I'm calling it like this:
$userId = 60;
$title = "TestTitle";
$contents = "Lorem ipsum dolor sit amet";
$date = 1356173771;
echo "creating new activity\n";
$a = new Activity($userId, $title, $contents, $date);
Placing echo's in the constructors revealed that Article::__construct() was not used and it went right to DBObject::__construct().
Edit 2: This is a version that should be working properly: http://ideone.com/VJzdI3 . I'm using PHPUnit for testing. This is the output if i run with PHPUnit:
creating new activity
DBObject constructed called
QUERY: 60 ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '60' at line 1
The latter means it's trying to initialize it using meekrodb; That should only happen if i call new on a subclass of DBObject with something other than null or an array. However, since Article should override __construct, that one should be called first.
Your understanding is correct: with the setup you describe creating a new Activity would definitely "defer" to Article::__construct. If it does not then your description and the actual code have to differ somewhere. PHP has its more than fair share of bugs, but this is a very simple scenario to attribute the surprising behavior to buggy code.
If you still think there is nothing wrong with the code, please post a working sample on an online codepad like htpp://ideone.com that exhibits the issue.
Related
I'm testing an object that is designed to test if user owns a given email. So on invocation of "tryEmail" method, it sends a message with confirmation link, to the given email address. My test looks like this:
public function testSendingWasSuccessful() {
$confirmationObject = $this->getMock('LT\EmailConfirmation\Model\ConfirmationObjectInterface');
$testType = 'test.type';
$testEmail = 'test#example.com';
$testData = [];
// EmailTester should create a new confirmation object.
$this->manager->expects(static::once())
->method('create')->with($testType, $testEmail)
->willReturn($confirmationObject);
// Then it should send the confirmation message.
$this->mailer->expects(static::once())
->method('send')->with(static::identicalTo($confirmationObject))
->willReturn(true);
// And save the confirmation object.
$this->manager->expects(static::once())
->method('save')->with(static::identicalTo($confirmationObject));
$tester = new EmailTester($this->repository, $this->manager, $this->confirmationHandler, $this->mailer);
static::assertTrue($tester->tryEmail($testType, $testEmail, $testData));
}
Now you can see what is possibly wrong with it - it contains multiple assertions. Why I decided to use those assertions inside of the one test? Because they are dependent on each other. So, the confirmation message should be sent only if new confirmation object has been created, and confirmation object should be saved only if confirmation message was send, and at the end, the output of the "tryEmail" method, using those mocked methods is being asserted.
However, I feel like I accidentally just described the implementation of the "tryEmail" method with my assertions. But it seems to be required for full coverage of this method, and making me sure that it always work as it should. I can imagine bugs passing by if I would remove any of those assertions. For example: static::identicalTo($confirmationObject) which is basically: check if the object passed to the mailer is the same as the one created before. If I would change interface of the mailer, I would have to change also this test of the EmailTester, so it seems like I'm doing something wrong here. At the same time however - how can I check for the above assertion without introducing this coupling? Or maybe should I just leave this untested?
Am I doing it right or wrong? How could I improve on it? When to use assertions on mocks really?
Added: I just had a thought - is it not true that testing class is supposed to test the implementation (if the implementation conforms with the interface)? That would mean that describing implementation in the test is actually a good thing, because it makes sure that implementation works correctly. That would also mean that level of coupling of the implementation will be carried over to the test, and is unavoidable. Am I wrong here?
The rule of "one assertion per test" is to keep your tests focused on one particular behavior of the code being tested. Having multiple assertions in a test is not a bad thing.
When using a mock object, I prefer to have some sort of assertions on methods being replaced. That way I ensure that the system will be using the dependencies as expected.
You testing class is to confirm behaviors of your code. The assertions that you have would be any of the checks that you would do manually to ensure that the class was behaving as you expected. Since you are expecting particular methods to be called in a specific manner, you want to have an assertion for them.
Issues that I see with the test are that you have a mock object returning a mock object. This is usually a code smell that means you are not passing the correct dependencies. You could possibly move the creation of your LT\EmailConfirmation\Model\ConfirmationObjectInterface object out of the method and pass that as a dependency of your method. Replacing the first two parameters of your method with this object.
You also don't seem to be using the third parameter at all in this test so it doesn't appear to be necessary.
I have the following method I want to test:
class SomeObject {
public function actionFromSomeController() {
$obj = new OtherObject();
$obj -> setAttributes();
$obj -> doAction();
}
}
class OtherObject {
private $_attr;
public function setAttributes() {
$this -> _attr = 'something';
Database :: execute('INSERT INTO table VALUES (' . $this -> _attr . ')');
$fileObj = new FileObj();
$content = $fileObj -> getSomeFileContent();
// do something else
}
public function doAction() {
echo $this -> _attr;
}
}
Now I want to test this method, its output depends on database content and one file on the server. It does a lot of things on the way, and the output is just one ID and success => 1.
How should I test it properly?
Some ideas on how to test small code pieces like this:
Generate test-data and pass it to your methods (also, fake database return data or file contents)
Use echo / var_dump() / die() to check property and variable content at different positions in your methods
Also use these commands to check whether execution reaches a certain point (for example to see whether a function got called or not)
If something doesn't work as expected without an error message: Check line by line with the above methods until you find the problem
Consider using interfaces and dependency injection if your code gets bigger - this is a bit over-the-top for this amount of code, but can be a tremendous time-saver when your application becomes big
Testing is never an automatic process and you will always have to think about what makes sense to do and what not. These things to do are never magic but basic PHP.
You should consider letting your scripts throw errors/exceptions if something goes wrong. Writing "silent" applications is almost never good since you can, if you really need a silent execution for production environments, just turn off error reporting and have the same effect. Many PHP functions return something special on failure and/or success and you can check for this. Database handlers do so, too. Do yourself a favor and use these return values!
I am writing fresh code, as part of refactoring an older legacy codebase.
Specifically, I am writing a Device class that will be used to compute various specifications of a device.
Device class depends on device's model number and particle count and I can call it as $device = new Device($modelNumber, $particleCount);
Problem: since this class will go into existing legacy code, I have no direct influence on if this class will be called properly. For Device to work, it needs to have correct model number and correct particle count. If it does not receive the proper configuration data, internally device will not be initialized, and the class will not work. I think that I need to find a way to let the caller know that there was an error, in case an invalid configuration data was supplied. How do I structure this to be in line with object oriented principles?
Or, alternatively, do I need to concern myself with this? I think there is a principle that if you supply garbage, you get garbage back, aka my class only needs to work properly with proper data. If improper data is supplied, it can bake a cake instead, or do nothing (and possibly fail silently). Well, I am not sure if this principle will be great. I do need something to complain if supplied configuration data is bad.
Here is some code of what I am thinking:
$device = new Device($x, $y);
$device->getData();
The above will fail or produce bad or no data if $x or $y are outside of device specs. I don't know how to handle this failure. I also want to assume that $device is valid when I call getData() method, and I can't make that assumption.
or
$device = new Device($x, $y);
if ($device->isValid())
$device->getData();
else
blow_up("invalid device configuration supplied");
The above is better, but the caller has to now they are to call isValid() function. This also "waters down" my class. It has to do two things: 1) create device, 2) verify device configuration is valid.
I can create a DeviceChecker class that deals with configuration vefication. And maybe that's a solution. It bothers me a little that DeviceChecker will have to contain some part of the logic that is already in Device class.
Questions
what problem am I trying to solve here? Am I actually trying to design an error handling system in addition to my "simple class" issue? I think I probably am... Well, I don't have the luxury of doing this at the moment (legacy code base is huge). Is there anything I can do now that is perhaps localized to the pieces of code I touch? That something is what I am looking for with this question.
I think you need to use below code to verify your passed arguments in construct
class Device {
public function __constructor($modelNumber, $particleCount) {
if(!$this->isValid($modelNumber, $particleCount) {
return false; //or return any error
}
}
}
This will check the passed params are valid or not and create object based on that only, otherwise return false or any error.
I am in the process of migrating a site from my personal dev server onto Windstream's business hosting server. I've already run into the issue of having developed using PHP 5.4 only to find out that my static functions won't work on WS's 5.1.4 installation. I've since fixed those issues and am not facing one that I can't seem to find any help for on the internet.
All of the static functions I was using have been rewritten as functions outside the class scope. Instead of having
class Product{
...
public static function myFunction(){}
...
}
I now have
function myFunction(){}
class Product{...}
in my included Product.php file.
However, when I try to call myFunction() from my code, nothing happens. I know the nothingness comes from WS's error handling, but the point is, the function isn't working. To verify this, I have taken the following steps:
Inserted the line echo "entered included"; immediately following the <?php in Product.php. This prints "entered included" on the index page, indicating that my include is working. I have done the same thing before the final ?> with the same results, so I don't think it's getting hung up inside the included file.
I have changed myFunction() in the included file to be simply
function myFunction(){echo "myFunction works";}
A call to myFunction() still makes nothing happen.
I have moved myFunction() to the including file (myFunction() now lives in index.php instead of Product.php; index includes Product.php). This time, myFunction() executes without issue.
To my 'hack it til it does what it should' sensibilities, this tells me that the server is having a problem with functions that are declared in files that are included; honestly, though, I have absolutely no clue what's going on, and any help would be appreciated. Their website is currently down, and they were expecting it to only be offline for a day, so I'll try pretty much anything short of sacrificing a fatted calf.
I know I should be posting more specific code, but since this is a customer's actual website, I'm trying to put as little of the actual code out here as is possible. I'm happy to append specific sections of code to this entry as they are requested for clarification.
Thanks in advance for your consideration.
#Rottingham: First, thanks for the 3v4l link. Second, my assumption about static methods in 5.4 vs 5.1.4 came from this line of php.net's page on static members and methods:
"As of PHP 5.3.0, it's possible to reference the class using a variable. The variable's value can not be a keyword (e.g. self, parent and static)."
src - http://www.php.net/manual/en/language.oop5.static.php
Since my version and the server version were on different sides of the 5.3 mark mentioned, I incorrectly assumed that this was my problem.
Third, when I get in from my day job, I'll update my code to show errors and update this post if a solution has not yet been found.
Ultimately, my problem isn't with using static methods (since I don't have them anymore) but with using any function that is declared in an included .php file.
I am writing a customer management system in PHP, for use offline (i.e. on the clients computer). I have considered using Java or C# but have come to the conclusion that it is easier to let the browser do all the layout for me, and just have the company install wamp on their computers.
Through this interface they will also be able to manage Agents (i.e. salesmen that go round their area getting orders for the company, in case any doesn't know). This is the section I will use in this post to demonstrate the problem I am having.
Basically I have 4 classes - AgentPages, AgentList, AgentDetails and AgentForm. AgentForm will have two modes - edit and new. AgentPages has a function called getPages, which returns an array of instances of the other 3 classes. However it does not like the "new" keyword.
My code is as follows (for the AgentPages class only):
<?php
require_once("AgentList.php");
require_once("AgentDetails.php");
require_once("AgentForm.php");
class AgentPages {
public function __construct() {
echo "Constructed";
}
private $pages = array("List" => new AgentList(), "Details" => new AgentDetails(), "Form" => new AgentForm());
function getPages() {
return $this->pages;
}
}
?>
I am using the netbeans 6.9 IDE with PHP enabled, and (as you can probably guess) I have wamp server installed. Under PHP version 5.3 the netbeans debugger is telling me that "Parse error: parse error in C:\wamp\www\CustomerApp_v2\Agents\AgentPages.php on line 20". Under 5.2.11 it says something about unexpected T_NEW on that line. I have cut out a large comment on this, before line 20, but I can tell you that line 20 is the declaration of $pages. I have an empty constructor for each class at the moment.
I have also tried the following line instead of line 20:
$AgentList = new AgentList();
This doesnt work either - I get the same error. According to all the tutorials I have looked at there is nothing wrong with my code - I am probably just overlooking something obvious though.
Does anyone have any idea what I am doing wrong? I have done lots of PHP object oriented stuff before now, but the last time I touched it was 2 years ago.
Thanks in advance.
Regards,
Richard
The problem is that you're trying to initialize an instance variable in a declaration with an expression (a call to new is an expression). That doesn't work. Put the assignment in the constructor and it will work.
Like this:
class AgentPages {
public function __construct() {
$this->pages = array("List" => new AgentList(), "Details" => new AgentDetails(), "Form" => new AgentForm());
echo "Constructed";
}
private $pages;
}