I am working on an OOP implementation and I have the following:
abstract class Parent{
public abstract function select($order="desc");
}
class Child extends Parent{
public function select($order) // here is the problem error
{
// selection code
}
}
This throws an error that tells me the declaration must be compatible with the parent method.
I did implement it with the right parameters except I didn't carry over the default parameter setting.
I do not want to copy past the same prototype of parent method in 100 classes if i want someday change the default value. How can I do this?
does generic exist in php ??
public abstract function select($order="desc");
and public function select($order) dont match.
remove the default value from the abstract function.
About the only way I can see to avoid updating a lot of values if you ever want to change the common default is this:
abstract class Parent{
const DEFAULT_SELECT_ORDER = "desc";
public abstract function select($order = "");
protected static function select_order(&$order)
{
if (empty($order) || !in_array(strtolower($order), array("asc", "desc"))) {
// additional test to check if the value is valid
$order = self::DEFAULT_SELECT_ORDER;
}
}
}
class Child extends Parent{
public function select($order = "") // here is the problem error
{
self::select_order($order);
// selection code
}
}
Hmm - another, probably better approach:
abstract class Parent {
protected $order = "desc";
public function order($order) {
if (in_array(strtolower($order), array("asc", "desc"))) {
$this->order = $order;
} else {
// probably should throw an exception or return false or something
}
return true;
}
public abstract function select();
}
class Child extends Parent {
public function select() {
// select code using $this->order
}
}
$query = new Child();
$query->order("asc");
$results = $query->select();
All you need to do is change your extended method to be something like:
public function select($order="some other value") // here is the problem error
{
// selection code
}
Essentially because the original method has a default value then ALL overrides must have a default value.
In order to do what you want you would have to make $order an object property of Parent and change the method signature to get rid of the $order parameter. Then within any specific implementation you could simply set $order to whatever else you want.
You possible could use my tiny library ValueResolver, for example:
$myVar = ValueResolver::resolve($var, $default);
and don't forget to use namespace use LapaLabs\ValueResolver\Resolver\ValueResolver;
There are also ability to typecasting, for example if your variable's value should be integer, so use this:
$id = ValueResolver::toInteger('6 apples', 1); // returns 6
$id = ValueResolver::toInteger('There are no apples', 1); // returns 1 (used default value)
Check the docs for more examples
Related
I am trying to unit test a function which is in an entity class, and it is stored in my DB by the use of a constructor. Each time I am trying to test this function it is giving me that error
ArgumentCountError: Too few arguments to function App\Entity\Deal::__construct(), 0 passed in /var/www/html/casus/tests/dealsEntityFunctionsTest.php on line 10 and exactly 1 expected
It is obvious I think, but I am really new with unit testing and that stuff so I couldn't find the answer. Could you please help me?
My code is
class Deal
{
private bool $isNewToday
public function __construct($deal)
{
$this->isNewToday = $deal['is_new_today'];
}
public function getIsNewToday(): ?bool
{
return $this->isNewToday;
}
public function setIsNewToday(bool $isNewToday): self
{
$this->isNewToday = $isNewToday;
return $this;
}
}
And my unit test is
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal();
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
As brombeer suggested, new Deal entity requires parameter.
This parameter looks like an array, with key 'is_new_today'. So, sth like this below should help with constructor error.
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal(['is_new_today' => true]);
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
This has nothing to do with Unit Testing, or Symfony, or any of the other details you mentioned. You've defined something with a mandatory parameter, and then aren't passing that parameter.
Just like any function, the parameters to a constructor are mandatory unless you provide a default. And if you write code that assumes the parameter will have a particular format, you need to provide a value that meets that assumption.
So either pass the parameter every time you create the object, with whatever format the constructor expects:
$deal = new Deal(['is_new_today' => false]);
... or make it optional, and decide what should happen if it's not passed:
class Deal
{
private bool $isNewToday
public function __construct(?array $deal = null)
{
if ( isset($deal) ) {
$this->isNewToday = $deal['is_new_today'];
}
else {
$this->isNewToday = false;
}
}
}
Note that $isNewToday is defined as a non-nullable boolean, so you should always give it a value in the constructor, or an inline default, like private bool $isNewToday = false; Otherwise, you'll get "uninitialized value" errors if you try to read it. For that reason, the return type of ?bool on getIsNewToday() doesn't make sense - it can't return null, because $this->isNewToday can never be bool.
<?php
/*
* class: C_QUESTION_MULTIPLE_CHOICE_SINGLE_ANSWER
* properties:
* functions:
*/
class C_QUESTION_MULTIPLE_CHOICE_SINGLE_ANSWER extends C_QUESTION {
protected $q_type = 'T_MULTIPLE_CHOICE_SINGLE_ANSWER';
protected $q_type_info;
/*
* $choices:
* choices['A'] = array(answer = 'Y', description = '')
* choices['B'] = array(answer = 'N', description = '')
*/
protected $choices;
public $output_info = '';
public $count = 0;
function __construct($q_content, $option = 'load') {
// call parent's construct function to handle question base class information
// in parent construct function, it will call parent's load or save function by itself.
parent::__construct($q_content, $option);
if (! $this->op_status) {
// call function failed
return;
}
// handle multiple choice part
switch ($option) {
case 'save':
// $option = false, don't call parent::save() function
// parent construct function will call parent save function
$this->save($q_content, false);
break;
}
} // end of construct function
function save($q_content, $option = true) {
$this->count++;
$this->output_info .= '</br>'.$this->count.'</br>';
} // end of save function
}
In the above code, the sub-class function save is performed twice. If I don't call parent construct function:
/* don't call parent construct function
parent::__construct($q_content, $option);
if (! $this->op_status) {
// call function failed
return;
}
*/
The function save() will be performed once.
In the parent construct function, the parent class QUESTION will cal it's own save() function. The sub-class C_QUESTION_MULTIPLE_CHOICE_SINGLE_ANSWER override the parent function save(). So in the sub-class construction function, I manually call parent::__construct() function. But I don't why the sub-class save() function is called twice. I debug this by sub-class properties
public $output_info = '';
public $count = 0;
Please help me.
Thanks a lot.
=========================================================================================
I debuged several times after post the above question, found:
parent class QUESTION: __construct() function:
function __construct($q_content, $option = 'load') {
// set the operation status to true. If this operation fails, this will be set to false
$this->op_status = true;
switch ($option) {
case 'load':
if (!$q_content['qid']) {
$this->op_status = false;
drupal_set_message(t('question id :qid not exist. Please contact site administrator!', array(':qid' => $q_content['qid'])), 'error');
break;
}
$this->basic['qid'] = $this->extra['qid'] = $q_content['qid'];
$this->load($this->basic['qid']);
break;
case 'save':
**//$this->save($q_content);
self::save($q_content);**
break;
}
}
if I use $this->save to call parent itself save function, this statement not only call parent::save function, but also will call sub class::save function. It's very funny and confuse.
If I use self::save statement in the parent class QUESTION, then it won't call sub class::save function.
My question is, since I've already overrided parent::save function in the sub class, I should call parent::save, then call $this->save in the sub-class do something for subclass. Why when I call parent::save, it will perform both parent::save and sub-class::save. It really confuse me.
I tried to find answer in the php manual and internet, but I didn't find the relevant article can make me clear. They only say, self::function() is used for the static member function, and $this->function() is used for other member and function. But my function and member are not static. Or the default function and member are static?
Really appreciate for who can help me.
Thanks a lot.
In php OOP, $this-> referrer to the current instant of the class, whereas self:: referrer to the class where self:: is declared.self:: is also use to referrer to static functions and properties.
So, in your case, you have to change the call to your parent's save() function as self::save() within the parent's construct function.
Also, within your child class, you can call $this->save() to call the save function of the child, because you are overriding the parent's save function.you can also use self::save() within the child class.if you want to call parent's save function from a child class then you can use parent::save() in your child class.
No, default functions aren't static.to make them static you have to add static keyword in front of the function declaration.note that you can't declare __construct(),__deconstruct() as static.
Since your functions aren't static, when you referrer to them as self:: you are referring to the current class.it's called Scope Resolution Operator (::). also read this SO answer
Have a class that I am using, I am overriding variables in the class to change them to what values I need, but I also not sure if or how to handle an issue. I need to add a key that is generated to each of this URLs before the class calls them. I cannot modify the class file itself.
use Theme/Ride
class ETicket extends Ride {
public $key='US20120303'; // Not in original class
public $accessURL1 = 'http://domain.com/keycheck.php?key='.$key;
public $accessURL2 = 'http://domain.com/keycheck.php?key='.$key;
}
I understand that you cannot use a variable in the setting of the public class variables. Just not sure what would be the way to actually do something like this in the proper format.
My OOP skills are weak. I admit it. So if someone has a suggestion on where I could read up on it and get a clue, it would be appreciated as well. I guess I need OOP for Dummies. =/
---- UPDATE ---
The initial RIDE class has 2 URLs set.
public $accessURL1 = "http://domain.com/index.php";
public $accessURL2 = "http://domain.com/index2.php";
I was to override them so the RIDE class will use my new domains.
I can add the following and it works...
class ETicket extends RIDE {
public $accessURL1 = 'http://mydomain.com/myindex.php';
public $accessURL2 = 'http://mydomain.com/myindex2.php';
}
However, I also want to pass a variable from elsewhere ($key) as a parameter to the URL when I override them so when i call RIDE it has a URL with the value of KEY at the end. (?key=keyvalue)
Your close, if you do not want to allow calling code to change the $key, you can do something like:
class ETicket extends Ride {
public function getKey()
{
return 'US20120303';
}
public function generateUrl()
{
return 'http://domain.com/keycheck.php?key=' . $this->getKey();
}
}
// Calling code example
$eTicket= new ETicket();
// $key is a member of ETicket class, so just call on generateUrl which will
// build and return the url
var_dump($eTicket->generateUrl());
You can also permit calling code to change the key if needed, by adding a public setter/getter:
class ETicket extends Ride {
protected $key;
public function setKey($key)
{
$this->key = $key;
}
public function getKey()
{
return $this->key;
}
public function generateUrl()
{
return 'http://domain.com/keycheck.php?key=' . $this->getKey();
}
}
// Calling code example
$eTicket= new ETicket();
$eTicket->setKey('US20120303');
var_dump($eTicket->generateUrl());
-- UPDATE --
There are a couple of options, you can either append the key to your url as part of the calling code, like this:
$eTicket= new ETicket();
$url = $ride->accessURL1 . '?key=US20120303';
Or, use a method (changed slightly to accept key directly) as I described earlier:
class ETicket extends Ride
{
public function generateUrl($key)
{
return $this->accessURL1 . '?key=' . $key;
}
}
$eTicket= new ETicket();
$url = $eTicket->generateUrl('US20120303');
I guess the point is, you cannot do what you originally asked without which is to concatenate a variable to a member variable initialization.
I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.
I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.
ExampleClass
public function do_stuff($runTwice){
$results = do_cool_stuff();
if($runTwice){
$results = 2 * do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff()
{
return 2;
}
In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.
tl;dr
I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).
Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:
class ExampleClass {
public function do_stuff($runTwice) {
$results = $this->do_cool_stuff();
if ($runTwice) {
$results = 2 * $this->do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff() {
return 2;
}
}
class TestableExampleClass extends ExampleClass {
/** Stores how many times the do_cool_stuff method is called */
protected $callCount;
function __construct() {
$this->callCount = 0;
}
function getCallCount() {
return $this->callCount;
}
/** Increment the call counter, and then use the base class's functionality */
protected function do_cool_stuff() {
$this->callCount++;
return parent::do_cool_stuff();
}
}
class ExampleClassTest extends PHPUnit_Framework_TestCase {
public function test_do_stuff() {
$example = new ExampleClass();
$this->assertEquals(2, $example->do_stuff(false));
$this->assertEquals(4, $example->do_stuff(true));
}
public function test_do_cool_stuff_is_called_correctly() {
// Try it out the first way
$firstExample = new TestableExampleClass();
$this->assertEquals(0, $firstExample->getCallCount());
$firstExample->do_stuff(false);
$this->assertEquals(1, $firstExample->getCallCount());
// Now test the other code path
$secondExample = new TestableExampleClass();
$this->assertEquals(0, $secondExample->getCallCount());
$secondExample->do_stuff(true);
$this->assertEquals(2, $secondExample->getCallCount());
}
}
I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:
class ExampleClass {
public function do_stuff($runTwice) {
if ($runTwice) {
return $this->do_cool_stuff_twice();
} else {
return $this->do_cool_stuff_once();
}
}
//...
}
Try setting a global variable prior to utilizing the class.
$IAmDeclaredOutsideOfTheFunction;
then use it to store the count and simply check it after your functions and classes have been called.
we have a problem [cit.]
I need to assign a callback dynamically within a class, in base of a variable param: my goal is to have just one class (and not a main class and many extender sub-class), and inside this class if a value is X, then the funcitonX must be used, if is Y, the functionY.
I know i cant explain well, i hope my example will do:
class plzComplicateMyLife{
public $vehicle;
public $kindVehicle;
public $dynamicFunction;
public function __construct($vehicle, $kindVehicle){
$this->kindVehicle = $kindVehicle;
$this->vehicle = $vehicle;
switch($kindVehicle){
case 'cycle':
$this->dynamicFunction = "isACycle";
break;
case 'car':
$this->dynamicFunction = "isACar";
break;
}
//here come the problem, i need to call the callback store in dynamicFunction.
//i tried:
//call_user_func($this->$this->dinamicFunction, $this->vehicle);
//error: Catchable fatal error: Object of class plzComplicateMyLife could not be converted to string in [...]
//call_user_func("plzComplicateMyLife::".$this->dynamicFunction);
//Warning: call_user_func(plzComplicateMyLife::isACar) [function.call-user-func]: First argument is expected to be a valid callback in [...]
//$this->dynamicFunction();
//Fatal error: Call to undefined method plzComplicateMyLife::dynamicFunction() in [...]
//so, how can i do that?
}
public function isACycle($vehicle){
echo 'im a cycle, model: '.$vehicle.'<br />';
}
public function isACar($vehicle){
echo 'im a car, model: '.$vehicle.'<br />';
}
//i know this has no sense, in this example at least.
public function printKind(){
//call_user_func($this->$this->dinamicFunction, $this->vehicle);
//call_user_func("plzComplicateMyLife::".$this->dynamicFunction);
//then?
}
}
$maserati = new plzComplicateMyLife('maserati4', 'car');
//then, maybe, outside the class i'll need to recover the callback:
$maserati->printKind();
EDIT:
As Rob said, polymorphism would be really a good solution.
But the problem is that, in this case, i really must have the same declaration for every class instance, changing only the parameters...e.g:
$maserati = new plzComplicateMyLife('maserati4', 'car');
$ducati = new plzComplicateMyLife('maserati4', 'cycle');
//is good
//becose i cant have:
$maserati = new plzComplicateMyLifeWithACar('maserati4');
$ducati = new plzComplicateMyLifeWithACycle('maserati4');
Polymorphism is the way to go here but for future reference you can also do this:
public function printKind() {
$this->{$this->dynamicFunction}($this->vehicle);
}
In response to your edit, could you not do something like this instead?
abstract class MethodOfTransport {
protected $model;
public function __construct($model) {
$this->model = $model;
}
abstract public function printKind();
public static function create($model, $type) {
$object = new $type($model);
return $object;
}
}
class cycle extends MethodOfTransport {
public function printKind() {
echo 'im a cycle, model: '.$this->model.'<br />';
}
}
class car extends MethodOfTransport {
public function printKind() {
echo 'im a car, model: '.$this->model.'<br />';
}
}
$maserati = MethodOfTransport::create('maserati4', 'car');
$maserati->printKind();
$ducati = MethodOfTransport::create('maserati4', 'cycle');
$ducati->printKind();
In PHP you can use specify a method callback using an array as a callback variable (see here), for example:
array( $object, $methodName );
So you could do this
$callback = array($this, $this->dynamicFunction);
call_user_func($callback, $this->vehicle);
Er, why don't you want to use a simple inheritance structure here? If you want different behaviour depending upon the object modelled, then that's pretty much the canonical description of polymorphism.
If you really do want to plough on with callbacks into the same object, then you'll need to do one of two things:
Drop the $vehicle parameter from your callbacks, make them private or protected, and call into them normally, i.e.
call_user_func( array( $this, 'isACycle' ) );
Mark the callback as static, make them private or protected, and call into them as follows:
call_user_func( array( __CLASS__, 'isACycle' ), $this );
Within the non-static callback, access the object's properties via $this in the normal fashion. Note also that I suggest marking the callback as private or protected, in order to prevent unnecessary outside callers; presumably, you don't want them executing the wrong method for each type.