I have a problem in design my project code.
For example:
I have two model class: AClass and BClass which extends from CClass class.
For each of this class I have two service layer: AService and BService in which I have methods:
create(AClass $object) and create(BClass $object) corresponding.
So what I want. I want create Abstract Service CService in which I have abstract class create(CClass class). And my AService and BService must extends fromCService.
But in PHP I can't do this cause it failed for compatible exception.
So maybe some ideas how I can resolve this problem?
Update:
Maybe not very good example above, so I show you in real case:
abstract class Contract
{
protected $id;
protected $owner
}
class FundTraderContract extends Contract
{
private $fundContract;
}
class InvestorTraderContract extends Contract
{
private $investmentContract;
}
abstract class ContractService
{
abstract function create(Contract $contract){}
}
class FundTraderContractService extends ContractService
{
public function create(FundTraderContract $contract)
{
//some logic
}
}
class InvestorTraderContractService extends ContractService
{
public function create(InvestorTraderContract $contract)
{
//some logic
}
}
I think now example more easy-to-see. So in this case there is a compatible problem.
First off, apologies if this isn't exactly what you want - since you've stripped all the class names and just gone to AClass etc, it's really hard to actually understand the logic of your application.
http://en.wikipedia.org/wiki/Dependency_inversion_principle
Create an interface. Without knowing your actual classes it's impossible to give it a meaningful name, but you do this:
interface DInterface { }
class AClass implements DInterface { }
class BClass implements DInterface { }
class CClass
{
public function something(DInterface $class) { }
}
You can then define stuff inside DInterface that should be in both AClass and BClass, ie. if CClass::something expects to be able to call $class->all() then you can define public function all(); in DInterface, and PHP will error out if either AClass or BClass doesn't implement it.
Related
I was wondering if you could help me with the following question. First of all, I would like to tell you that if I am asking this question here, it is because I have already tried many options and none have worked for me. It turns out that I am developing a package with Laravel and I am using Laravel's dependency injection. But I am at a crossroads from which I have not found a way out. I'm trying to get the instance of a class in an intermediate method from a method chain, let me explain. Here is the code very similar to what I have:
PackageServiceProvider.php
<?php
class PackageServiceProvider extends ServiceProvider
{
public function register()
{
$this->configureBindings();
}
private function configureBindings()
{
$this->app->when(A_Class::class)->needs(B_Interface::class)->give(function () {
return new B_Class();
});
}
...
A_Class.php
<?php
class A_Class implements A_Interface
{
private $b_interface;
public function __construct(B_Interface $b_interface)
{
$this->b_interface = $b_interface;
}
public function create($arg1, $arg2)
{
return $this->b_interface->method_1()->call_another_method_from_another_class();
}
}
A_Interface.php
<?php
interface A_Interface extends Arrayable, Rendereable
{
public function create($arg1, $arg2);
...
}
<?php
class B_Class implements B_Interface
{
public function __construct()
{
// Here is the question...
// How could I get here the instance of the class A_Class?
}
public function method_1()
{
// should return another class instance
}
public function method_2()
{
// should return another class instance
}
}
B_Interface.php
<?php
interface B_Interface
{
public function method_1();
public function method_2();
...
}
If you look at class B_Class``, in the __constructmethod I'm trying to get the instance of classA_Class``` from where that class is being called. I have tried the following:
class B_Class implements B_Interface
{
public function __construct(A_Interface $a_interface)
{
// Here is the question...
// How could I get here the instance of the class A_Class?
}
But I get the following error:
Segmentation fault
I guess there must be some way I can achieve what I need. I would appreciate any help in advance.
Because you are referring to class A inside your class B constructor, and class B in your class A constructor, you have introduced a cyclic dependency.
This will resolve to the error you are experiencing, which is the segmentation fault, as outlined here:
https://laravel.io/forum/11-08-2016-circular-dependency-causes-segmentation-fault-error-when-running-php-artisan-optimize
So the answer is to remove the cyclic dependency if possible, as you can have methods from A calling B that calls A for infinity at runtime, and you will get the above error above again.
If your class A and B are relatively small, I would recommend combining them before using a cyclic dependency.
For interest and prosperity, if you want achieve a cyclic dependency, this is possible by registering your Class A with a singleton from inside A's constructor, and putting the reference to the incomplete object into Class B with your code above. I try with laravels singleton here, its untested, but hopefully you'll get the idea.
class A_Class implements A_Interface
{
public function __construct(B_Interface $b_interface)
{
//I dont think you can pass $this to a function when construction is incomplete, hence $that.
$that = $this;
App::singleton('A_Class', function($that){
return $that;
});
$this->b_interface = $b_interface;
}
}
class B_Class implements B_Interface
{
public function __construct(A_Interface $a_interface)
{
//unfinished object, but no more error.
$this->aClass = App::make('A_Class')
}
}
Is it possible to override an abstract function in PHP 7 with a function in a child class that would narrow down on the accepted argument type?
A word of elaboration - let's assume that I have an abstract method:
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
Now in one child class, I'd like to override this method's signature in order for it to accept only a specific parameter type (let's assume that EmailDeliveryTarget extends AbstractDeliveryTarget):
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery;
PHP interpreter would then complain that the signature of the abstract method cannot be altered in the child class. Is there, then, a way to achieve that type-safety in a child class other than some in-method-body type guards?
This is not a technical limitation; what you're asking for doesn't make sense with the principles of OOP.
Your abstract class is a contract; let's define the base class:
class AbstractDeliverer {
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
}
And some delivery targets
class AbstractDeliveryTarget {}
class EmailDeliveryTarget extends AbstractDeliveryTarget {}
class SMSDeliveryTarget extends AbstractDeliveryTarget {}
Then you can write this somewhere else:
function deliverAll(AbstractDeliverer $d) {
$e = new EmailDeliveryTarget;
$d->setTarget($e);
$s = new SMSDeliveryTarget;
$d->setTarget($s);
}
Because we know that $d is an AbstractDeliverer, we know that passing $e and $s to it should both work. That is the contract guaranteed to us when we made sure our input was of that type.
Now lets see what happens if you extend it the way you wanted:
class EmailOnlyDeliverer extends AbstractDeliverer {
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery {
/* ... */
}
}
$emailonly = new EmailOnlyDeliverer;
We know that $e instanceOf AbstractDeliverer will be true, because we've inherited, so we know we can safely call our deliverAll method:
deliverAll($emailonly);
The first part of the function is fine, and will effectively run this:
$e = new EmailDeliveryTarget;
$emailonly->setTarget($e);
But then we hit this part:
$s = new SMSDeliveryTarget;
$emailonly->setTarget($s);
Oops! Fatal error! But the contract on AbstractDeliverer told us this was the correct value to pass! What went wrong?
The rule is that a sub-class must accept all inputs that the parent class would accept, but it can accept additional inputs if it wants, something known as "contravariance". (Return types are instead "covariant": the sub-class must never return a value which couldn't be returned from the parent class, but can make a stronger promise that it will only return a subset of those values; I'll leave you to come up with an example of that one yourself).
Looks like no pretty solutions so we can try use interfaces.
<?php
interface DeliveryTarget {}
class AbstractDeliveryTarget implements DeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget{
}
abstract class Q {
abstract protected function action(DeliveryTarget $class);
}
class Y extends Q {
protected function action(DeliveryTarget $class){}
}
OR
interface DeliveryTargetAction {}
class AbstractDeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget implements DeliveryTargetAction {
}
abstract class Q {
abstract protected function action(DeliveryTargetAction $class);
}
class Y extends Q {
protected function action(DeliveryTargetAction $class){}
}
Depends on what you want to do.
I have an abstract class that declares the methods required to its children. It also has a construstor that its children inherits. How can I make the abstract class affect the children of the classes that extends it. To further clarify my question, here is my case:
The Abstract Class (abstract.php):
<?php
include_once 'database.php';
include_once 'validation.php';
abstract class DataOperations extends DatabaseConnection {
//The error string shared by all children of DataOperations
//This will be the message to be displayed in case validation failure
public $validator;
public $err_valid_string;
/**
* The DataOperations' constructor ensures that all of its children can perform database operation
* by automatically starting it for them. In case a child overrides this constructor, this child
* must explicitly start the connection to prevent fatal errors. Also, $validator must be re-instantiated
*/
public function __construct() {
$this->startDBConnection();
$this->validator = new InputValidator();
}
public function __destruct() {
}
abstract public function validateData();
abstract public function loadRecord($key, $cascade);
abstract public function saveRecord();
abstract public function updateRecord();
abstract public function deleteRecord();
}
?>
Now, here is the child object that extends the DataOperations abstract class
class Guest extends DataOperations {
//some properties here
public function validateData() {
//implementation
}
public function newRecord(implementation) {
//implementation
}
public function loadRecord($key, $cascade){
//implementation
}
public function saveRecord() {
//implementation
}
public function updateRecord() {
//implementation
}
public function deleteRecord() {
//implementation
}
}
?>
And here is another class, which is a child of Guest
class Booking extends Guest {
//some properties here
public function validateData() {
//implementation
}
public function newRecord(implementation) {
//implementation
}
public function loadRecord($key, $cascade){
//implementation
}
public function saveRecord() {
//implementation
}
public function updateRecord() {
//implementation
}
public function deleteRecord() {
//implementation
}
}
?>
The problem is, if I remove a method in Booking, say deleteRecord(), PHP won't throw an error because I think abstract class doesn't affect its 'grandchildren'. How can I fix this? I thought of using interfaces but my system already has 11 classes that depends to some methods of the abstract class. It will require intensive refactoring.
As you himself stated interface is best suited solution. Like
include_once 'database.php';
include_once 'validation.php';
interface DbInterface {
abstract public function validateData();
abstract public function loadRecord($key, $cascade);
abstract public function saveRecord();
abstract public function updateRecord();
abstract public function deleteRecord();
}
class DataOperations extends DatabaseConnection {
//The error string shared by all children of DataOperations
//This will be the message to be displayed in case validation failure
public $validator;
public $err_valid_string;
/**
* The DataOperations' constructor ensures that all of its children can perform database operation
* by automatically starting it for them. In case a child overrides this constructor, this child
* must explicitly start the connection to prevent fatal errors. Also, $validator must be re-instantiated
*/
public function __construct() {
$this->startDBConnection();
$this->validator = new InputValidator();
}
public function __destruct() {
}
}
class Guest extends DataOperations implements DbInterface {
- - -
}
class Booking extends Guest implements DbInterface {
- - -
}
First as you see I removed abstract from parent class as I assuming only those methods are abstract. Second as per your problem of 11 classes depend on Abstract class, I would say As you only remove abstract methods, Class implementing abstract methods now should implement interface. It is one time needed task. While classes using other normal methods of abstract class work like previous.
The best and cleanest way would be to have your "BOOKING" class extend the "DATAOPERATIONS" class, instead of GUEST, because looks like you don't have any extra methods in the BOOKING class. other wise make and interface and implement it. That is not the preferred way but you would have to give more info your situation.
To be clear, re-declaring a method in a child class will overwrite the parent class's implementation of that method when called from the child class, while not affecting any additional functionality provided by extending the parent class:
class a
{
function hello()
{
echo "Hello";
}
function goodbye()
{
echo "Goodbye";
}
}
/**
* class b overwrites class a's implementation of method goodbye but will retain
* it's definition for method hello
*/
class b extends a
{
function goodbye()
{
echo "See ya!";
}
}
$object = new b();
$object->hello(); // Hello
$object->goodbye();// See ya!
It appears that you want to implement a consistent interface across multiple class definitions. If this is the case, you will likely want to explore using PHP's interfaces.
These allow you to specify the methods that must exist in your class definition along with their set of arguments (collectively known as the signature). Your class definitions will implement an interface and if your definition does not meet the interface implementation specification, a fatal error will be thrown.
From the PHP manual:
// Declare the interface 'iTemplate'
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// Implement the interface
// This will work
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
// This will not work
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
You can find more information about interface in the PHP manual:
http://us2.php.net/interface
Finally, it looks like you are hoping to define a common constructor for the child classes. Your child classes can both extend the DataOperations class while implementing a separate interface:
class Guest extends DataOperations implements DatabaseWriter
...
I have a parent class that has a public function that is final. Is it possible to disallow access to this method in a derived class? Other classes that derive from the parent class need to still be able to call it.
class ParentClass {
final public function test() {
}
}
class ChildClass extends ParentClass {
}
class ChildClass2 extends ParentClass {
}
$child = new ChildClass();
$child2 = new ChildClass2();
// make this illegal:
$child->test();
// but not this:
$child2->test();
When you inherit from class it means that derived class is a class that inherits. It means that it has exact the same base functionality that can be expanded, overriden and so on. If it makes no sense that derived class had method that is defined in parent class means that derived class is not a class that it derives.
For real world example let's say you have class FlyingMachine that has method Fly and Move, it would not be logical if you derived class Car from FlyingMachine because cars don't fly, while deriving class AirPlane from mentioned base class is perfectly fine. But it would be logical if you had base class Machine that had method Move and car was derived from this class Car : Machine (car is a machine, right?), FlyingMachine is a machine too so FlyingMachine : Machine is perfectly fine and since air-plane is a flying machine AirPlane : FlyingMachine makes sense.
For your given example this should be something like:
abstract class VeryBaseClass {
public function MethodThatIsLogicalForEveryDerivedClass() {}
}
abstract class BaseClass extends VeryBaseClass {
public function SomeOtherFunctionThatMakesSenseOnlyForSomeClasses() {}
}
class ClassThatShouldNotHaveSpecificFunctionDefinedInBaseClass extends VeryBaseClass{}
class ClassThatShouldHaveSomeOtherMethod extends BaseClass {}
Edit:
If there's a functionality that several derived classes need but for some of them it should be public and for some of them not. You should define in base class this method as protected (derived class can access one but from outside impossible). And in derived class that require that this method was public make another method and call parent's method. E.g.:
abstract class Car { protected function Shift() {} }
class ManualCar extends Car {
public function Shift() { parent::Shift(); }
}
class AutomaticCar extends Car {
//Some implementation of automatic car that uses protected Shift method
}
Little demo
How about checking for that class in the method, and throwing an exception if it's the disallowed class:
class ParentClass {
final public function test() {
if (get_class($this) == "ChildClass")
throw new Exception("ChildClass cannot call test()")
}
}
NB: this is fairly evil
I think there is a problem in php's OOP implementation.
EDIT: Consider more illustrative example:
abstract class Animal {
public $name;
// public function Communicate(Animal $partner) {} // Works
public abstract function Communicate(Animal $partner); // Gives error
}
class Panda extends Animal {
public function Communicate(Panda $partner) {
echo "Hi {$partner->name} I'm a Panda";
}
}
class Human extends Animal {
public function Communicate(Human $partner) {
echo "Hi {$partner->name} I'm a Human";
}
}
$john = new Human(); $john->name = 'John';
$mary = new Human(); $mary->name = 'Mary';
$john->Communicate($mary); // should be ok
$zuzi = new Panda(); $zuzi->name = 'Zuzi';
$zuzi->Communicate($john); // should give error
The problem is that when Animal::Communicate is an abstract method, php tells that the following methods are illegal:
"public function Communicate(Panda $partner)"
"public function Communicate(Human $partner)"
but when Animal::Communicate is non-abstract but has zero-implementation Php thinks that these methods are legal. So in my opinion it's not right because we are doing override in both cases, and these both cases are equal, so it seems like it's a bug...
Older part of the post:
Please consider the following code:
Framework.php
namespace A
{
class Component { ... }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
Implementation.php
namespace B
{
class MyComponent extends \A\Component { ... }
}
MyDecorator.php
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) { ... }
}
}
The following code gives error in MyDecorator.php telling
Fatal error: Declaration of MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in MyDecorator.php on line ...
But when I change the Framework.php::Decorator class to the following implementation:
abstract class Decorator {
public function Decorate(\A\Component $component) {}
}
the problem disappears.
I'm not sure (haven't tested it ;), but you declare this abstract function:
public abstract function Decorate(\A\Component $component);
So you should implement this EXACTLY like that. But you did this:
public function Decorate(\B\MyComponent $component) { ... }
That's not the same. Could you try to change that to \A\Component?
To all comments: fact of the matter is that this piece of PHP "runs"
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\A\Component $component) {}
}
}
And this doesn't:
<?php
namespace A
{
class Component { }
abstract class Decorator {
public abstract function Decorate(\A\Component $component);
}
}
namespace B
{
class MyComponent extends \A\Component { }
}
namespace A
{
class MyDecorator extends Decorator {
public function Decorate(\B\MyComponent $component) {}
}
}
?>
With this error: PHP Fatal error: Declaration of A\MyDecorator::Decorate() must be compatible with that of A\Decorator::Decorate() in line 18
Now you can discuss all you like about how that should or should not be, but that's the problem with the code.
so, to satisfy my own curiosity: this is illegal too:
<?php
class Component { }
abstract class Decorator {
public abstract function Decorate(Component $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(MyComponent $component) {}
}
?>
It's not the namespaces or anything. It just doesn't seem legal.
See http://bugs.php.net/bug.php?id=36601, this issues has been reported as a bug but was rejected because of laziness :D
It has nothing to do with it being abstract. It has to do with the type hinting. The two definitions of the method are not compatible because you explicitly set the argument to be of type \A\Component and then try to overload the method with \B\Component you cant do that because it changes the method signature. Any subsequent declaration of Decorate must use the same type hint as its parent declaration in order for the method signatures to be compatible.
This might assist someone, and am not late.
The best way to handle such is by using an interface.
Consider below;
<?php
interface Componentor{}
class Component implements Componentor { }
abstract class Decorator {
public abstract function Decorate(Componentor $component);
}
class MyComponent extends Component { }
class MyDecorator extends Decorator {
public function Decorate(Componentor $component) {}
}
?>
Usage;
<?php
$c=new Component();
//TODO ....blah blah blah...
$decor=new MyDecorator();
//TODO ....blah blah blah...
$decor->Decorate($c);
?>