I'm following along with some learning material on PHP and am now into abstract classes and methods as well as interfaces. While implementing interfaces I've encountered an error on first run-through. It happens while the classes and interfaces are being defined. I apologize in advance for the size of the code sample but I'd like to be circumspect.
The error I receive is:
'Fatal error: Declaration of DinnerMenu::setDinnerPortion() must be compatible with DinnerPortion::setDinnerPortion() in ... Menu.php on line 85'.
Menu.php follows:
<?php
abstract class Menu { // can't be instanciated, only extended from
private $_menuid,
$_menuitemid,
$_menuname,
$_description;
public function setMenuID($menuid) {$this->_menuid = $menuid;}
public function getMenuID() {return $this->_menuid;}
public function setMenuItemID($menuitemid) {$this->_menuitemid = $menuitemid;}
public function getMenuItemID() {return $this->_menuitemid;}
public function setMenuName($menuname) {$this->__menuname = $menuname;}
public function getMenuName() {return $this->_menuname;}
public function setDescription($description) {$this->_description = $description;}
public function getDescription() {return $this->_description;}
}
class MenuItem {
private $_menuitemid,
$_itemname,
$_description,
$_price,
$_servingsize,
$_picture;
public function setID($menuitemid) {$this->_menuitemid = $menuitemid;}
public function getID() {return $this->_menuitemid;}
public function setItemName($itemname) {$this->_itemname = $itemname;}
public function getItemName() {return $this->_itemname;}
public function setDescription($description) {$this->_description = $description;}
public function getDescription() {return $this->_description;}
public function setPrice($price) {$this->_price = $price;}
public function getPrice() {return $this->_price;}
public function setServingSize($servingsize) {$this->_servingsize = $servingsize;}
public function getServingSize() {return $this->_servingsize;}
public function setPicture($picture) {$this->_picture = $picture;}
public function getPicture() {return $this->_picture;}
}
class MainMenu extends Menu {
}
class DrinkMenu extends Menu {
}
class LunchMenu extends Menu {
}
final class KidsMenu extends Menu { // final keyword stops inheritance: cannot have sub-classes or child classes, cannot be overridden
}
final class DessertMenu extends Menu {
}
interface DinnerPortion {
public function setDinnerPortion();
}
interface DinnerPrices {
public function setDinnerPrices();
}
interface HappyHourDrinkPrices {
public function setHappyHourDrinkPrices();
}
final class DinnerMenu extends LunchMenu implements DinnerPortion, DinnerPrices {
public function setDinnerPortion($menuitemObject) {
$adjusted_servingsize = 1;
$base_servingsize = $menuitemObject->getServingSize();
// dinner portion 50% bigger than lunch portion
$adjusted_servingsize = $base_servingsize * 1.5;
return $adjusted_servingsize;
}
public function setDinnerPrices($menuitemObject) {
$adjusted_price = 1;
$base_price = $menuitemObject->getPrice();
// dinner price 25% more than lunch price
$adjusted_price = $base_price * 1.25;
return $adjusted_price;
}
}
final class HappyHourMenu extends DrinkMenu implements HappyHourDrinkPrices {
public function setHappyHourDrinkPRices($drinkObject) {
$adjusted_price = 1;
$base_price = $drinkObject->getPrice();
// happy hour drink prices 30% less than regular prices
$adjusted_price = $base_price * 0.7;
return $adjusted_price;
}
}
?>
setDinnerPortion in the interface doesn't have an argument, while the same method in the class DinnerMenu (which implements the interface) has the argument $menuitemObject.
An interface describes rules that a class must obey. In this case it defines that the class needs to implement this method without arguments.
If you want to use interfaces with their methods, you have to take over the methods which are described in the interface.
From your interface ;
public function setDinnerPortion($menuitemObject)
The above method requires an argument while the method in your class which implements the interface doesn't have it
public function setDinnerPortion()
Solution : add an argument.
in your class the signature is :
public function setDinnerPortion($menuitemObject)
although in your interface the function signature is :
public function setDinnerPortion();
These function signatures must be the same !
Related
Suppose I have the following :
<?php
class Final extends Intermediate {
public function final_level() {
$this->low_level();
$this->inter_level();
}
}
class Intermediate extends Lib1 {
public function inter_level() {
$this->low_level();
}
}
class Lib1 {
public function low_level1();
public function low_level2();
}
class Lib2 {
public function low_level1();
public function low_level2();
}
I would like to change the Intermediate class to extend Lib1 or Lib2, depending on some conditions, without duplicating Intermediate and Final code content.
All low_level functions are the same for both Lib.
In the end, I would like to have a Final1 class that use Lib1 (and Final2 that use Lib2).
How could I achieve this ?
You cannot achieve this via inheritance but you can via delegation
With this approach you delegate the implementation of some methods to a 'delegate' object rather than a base class.
Here it is an example:
<?php
class Final extends Intermediate {
public function __construct(Lib delegate) {
parent::__construct(delegate);
}
public function final_level() {
$this->low_level();
$this->inter_level();
}
}
class Intermediate implements Lib { //here you implement an interface rather than extending a class
private Lib delegate;
public function __construct(Lib delegate) {
$this->delegate = delegate;
}
public function inter_level() {
$this->low_level();
}
public function low_level() {
//delegate!
$this->delegate->low_level();
}
}
class Lib1 implements Lib{
public function low_level(); //implementation #1
}
class Lib2 implements Lib {
public function low_level(); //implementation #2
}
interface Lib {
public function low_level();
}
now you can create your final1 and final2 object in this way:
$final1 = new Final(new Lib1());
$final2 = new Final(new Lib2());
or, if you prefer, you can create the Final1 and Final2 classes extending from Final:
class Final1 extends Final {
public function __construct()
{
parent::__construct(new Lib1());
}
}
class Final2 extends Final {
public function __construct()
{
parent::__construct(new Lib2());
}
}
$final1 = new Final1();
$final2 = new Final2();
Say I have the following code, is there a way to somehow extend an abstract class on a child and require a different type of argument in the "overloaded" function. I want to insert various types of objects in the Collection through the add function. In some cases, I'd like to insert an Error object, sometimes some other (XYZ) object, and let's say that all those objects extend the same abstract class called Parent.
I would appreciate if somebody could tell me if something like this is even possible, and if it is suggest some ways to accomplish this. Note that production server on which I intend to host the application runs on php 5.6.40.
Thank you in advance.
namespace App;
use App\Models\Parent;
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
abstract public function add($key, Parent $item);
}
public class ErrorList extends Collection
{
public function __construct()
{
parent::__construct();
}
public function add($key, Error $item)
{
$this->collection[$key] = $item;
}
}
namespace App\Models;
abstract class Parent {}
public class Error extends Parent {}
public class XYZ extends Parent{}
Try this
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
//no type hinting
abstract public function add($key, $item);
}
class ErrorList extends Collection
{
// this constructor doing nothing , it can be removed and
// parent constructor will still be called unlike java or any other
// OOP
public function __construct()
{
parent::__construct();
}
//no type hinting
public function add($key, $item)
{
//code
}
}
If you're extending a class or implementing an interface the signature must match. You can however implement type checking yourself and type hint in a docblock.
As a side note, public class is invalid syntax.
abstract class Collection
{
protected $collection;
public function __construct()
{
$this->collection = array();
}
abstract public function add($key, Parent $item);
}
class ErrorList extends Collection
{
public function __construct()
{
parent::__construct();
}
/**
* #param $key
* #param Parent|Error $item
*/
public function add($key, Parent $item)
{
if (!($item instanceof Error)) {
throw new \InvalidArgumentException('Unable to add object to error list: ' . get_class($item));
}
$this->collection[$key] = $item;
}
}
From extended, I want call a parent function like this
file 1 > Main Class:
class CheckoutProcessCore implements Interface
{
public function __construct(
Context $context,
CheckoutSession $checkoutSession
) {
$this->context = $context;
$this->smarty = $this->context->smarty;
$this->checkoutSession = $checkoutSession;
}
public function getSteps()
{
return $this->steps;
}
}
file 2 > Extended Class:
class Basket extends CheckoutProcessCore
{
protected static $basket_pid = null;
function __construct() {
parent::__construct();
}
public static function test() {
$step = parent::getSteps(); <-- the Problem!
[ ... ]
}
}
How can I call getSteps from test without any errors ?
What's wrong in my Code ?
Thank you
I'm not so sure if this might help/solve your problem. However,
You might have forgotten adding your files on the top of pages that might be required to do so, if you are not using autholoader/composer:
require_once('/path/to/CheckoutProcessCore.php');
require_once('/path/to/Basket.php');
Also, you probably are not allowed to use Interface as an identifier, for which you may look into this post, which says Interface is a reserved word. You cannot most likely use other variances of Interface, such as (interface, iNterface, etc.) as an identifier.
require_once('/path/to/InterfaceSomeAddedNameThatYouWish.php');
Then, you might consider modifying:
class CheckoutProcessCore implements InterfaceSomeAddedNameThatYouWish
{
public function __construct(
Context $context,
CheckoutSession $checkoutSession
) {
$this->context = $context;
$this->smarty = $this->context->smarty;
$this->checkoutSession = $checkoutSession;
}
public function getSteps()
{
return $this->steps;
}
}
Is there any way in php to make sure that a class can be extended by one and only one class?
I have some code that illustrates what I'm trying to do, basically I have a DB manager class and a DB query class that is extended by the manager class. What I'd like to do is make sure that the DB query class can only be used by the DB manager class.
The code below works, but it seems very rough. In the code I delcare the query class abstract with a single abstract function that checks the classname, or I could simply declare all of the Manager functions as abstract in the query class (which seems hacky). If there is a simpler way to do this than my code below that would be very useful...
abstract class DB_Query {
private static $HOST = 'localhost';
private static $USERNAME = 'guest';
private static $PASSWORD = 'password';
private static $DATABASE = 'APP';
//////////
/* USING ABSTRACT FUNCTION HERE TO ENFORCE CHILD TYPE */
abstract function isDB();
/* OR USING ALTERNATE ABSTRACT TO ENFORE CHILD TYPE */
abstract function connect();
abstract function findConnection();
abstract function getParamArray();
//////////
private function __construct() { return $this->Connect(); }
public function Read($sql) { //implementation here }
public function Query($sql) { //implementation here }
public function Fetch($res, $type='row', $single='true') { //implementation here }
}
class DB extends DB_Query {
public $connections = array();
public static $instance;
public function isDB() {
if (get_parent_class() === 'Database' && get_class($this)!=='DB') {
throw new \Exception('This class can\'t extend the Database class');
}
}
public function connect($host=null,$user=null,$pass=null,$db=null) { //implementation here }
function findConnection($user, $password=null) { //implementation here }
public function getParamArray($param) {}
public function threadList() {}
public function getThread($threadId=null) {}
public static function Singleton() { //implementation here }
private function __construct() { //implementation here }
}
I would go after marking the constructor of DB_Query as final and implementing it the way that it checks the instance and fires some exception. Something like this
class Base {
final function __construct() {
if (!$this instanceof Base && !$this instanceof TheChosenOne) {
throw new RuntimeException("Only TheChosenOne can inherit Base");
}
/**
* use this function as constructor
*/
$this->__internal_base_construct();
}
protected function __internal_base_construct() {
// constructor code
}
}
But your problem is rather strange and kind of breaking the idea of OOP in several ways. Just combine it into a single class and use final class directive.
http://php.net/manual/en/language.oop5.final.php
class Database_Query extends Database {
public static $instance;
public function Query($sql) {}
public function Fetch($res, $type='row', $single='true') {}
public static function Singleton() {}
private function __construct() {
$this->link = $this->connect()->find('guest')->getLink();
}
}
I have a requirement where a process can be implemented in 2 different situation. One situation would be where the start date cannot be in the past and the other would be where it can.
Currently we utilise Value Objects where we perform a series of a validation items on each field submitted using Zend Validate objects.
The validation extends a base class e.g.
class ValueObject_test1 extends filter()
Filter is made up of: -
class filter {
protected $_filter;
protected $_filterRules = array();
protected $_validatorRules = array();
protected $_data = array();
protected $_objData = array();
protected $_options = array(Zend_Filter_Input::ESCAPE_FILTER => 'StripTags');
protected $_runValidation = true;
protected function _setFilter()
protected function _addFilter()
protected function _addValidator()
protected function _addData()
protected function _addObject()
protected function _addOption()
public function getData()
public function hasErrors()
public function getMessages()
public function getValidationState()
public function __get()
public function __isset()
public function __set()
}
ValueObject_test1 is made up of:
class ValueObject_test1 extends filter {
public function __construct($testVar) {
$this->_setData(testVar);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar) {
$this->_addData('testVar', $testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
What I'm trying to achieve is an extension of ValueObject_test1 so that my second situation will have an additional validation item as well as the items in ValueObject_test1()
I've written the following for my second situation:-
<?php
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar', $testVar);
$this->_addData('testVar2', $testVar2);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
The issue i'm having is that the output from this only appears to validate my second situation validation and nothing on the second. I'm under the impression that by setting both variables in _setData() the validation should occur for items in ValueObject_test1 and the items in my ValueObject_test2?
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
parent::__construct($testVar);
}
// _setFilters is identical to the parent implementation
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar2', $testVar2);
parent::_setData($testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
parent::_setValidators();
}
}
My code doesn't call the _setData correctly because the $this->setData(testVar) inside the parent::_construct's will call the $this->_setData(testVar) version of the function.
If you want to override a function, your override will likely want to also run the logic of the parent.
<?php
class Foo {
protected function _setFoobar() {
// Foo logic
}
}
class Bar extends Foo {
protected function _setFoobar() {
// custom Bar logic specific to the child class (Bar)
parent::_setFoobar();
}
}