Class inheritance, code factorisation, php object model - php

Say I have an abstract class Entity, I also have a handful of abstract classes Provider, Model, Service,... all extending directly Entity. Finally, i have a last set of concrete classes ConcreteProvider, ConcreteModel, ... that extends respectively Provider, Model, ...
On each instance of Concrete* I want a method getId() that when called on an instance of a class which extends Provider ( like ConcreteProvider ), it returns the string 'Provider', and so on for Model and ...
The only way I found to achieve that is :
abstract class Entity {
abstract function getId ();
}
abstract class Provider extends Entity {
function getId () {
return __CLASS__;
}
}
abstract class Model extends Entity {
function getId () {
return __CLASS__;
}
}
class ConcreteProvider extends Provider {
}
class ConcreteModel extends Model {
}
$cp = new ConcreteProvider();
echo $cp->getId() . "\n"; // returns the string Provider
$cm = new ConcreteModel();
echo $cm->getId(); // returns the string Model
Code duplication is here obvious. I'm looking for the same behaviour without duplicate. I guess it would be nice to put the method into Entity.
Have you an idea ? How would you do that ?
// Edit
Of course, there is a way to move getId into Entity :
abstract class Entity {
function getId () {
$class = get_called_class();
while ( __CLASS__ !== get_parent_class($class) )
$class = get_parent_class($class);
return $class;
}
}
But there is a lot of computation for, imo, nothing worth...

I'm not a big fan of magic, thus I recommend the straight-forward way
const ID = 'Provider';
public function getId () {
return self::ID;
}
Or just
function getId () {
return 'Provider';
}
but with the constant its easier to compare
$x->getId() == Provider::ID;

Here is the best I found, using my second proposition, and adding to it memoization.
abstract class Entity {
private $subType = '';
function getId () {
if ( $this->subtype )
return $this->subType;
$class = get_called_class();
while ( __CLASS__ !== get_parent_class($class) )
$class = get_parent_class($class);
return $this->subType = $class;
}
}

<?php
class Entity {
public static function getId() {
return get_called_class();
}
}
class Model extends Entity {
}
class Provider extends Entity {
}
$a = new Model();
echo $a->getId(); //returns Model
$b = new Provider();
echo $b->getId(); //returns Provider
$c = new Entity();
echo $c->getId(); //returns Entity
Using late static binding. Your php version should be greater than 5.3.
http://www.php.net/manual/en/function.get-called-class.php

You should use PHP magic method __toString() for the classes you want to get class names for, then when you typecast the object as a string, it will return the class name:
abstract class Provider extends Entity {
public function __toString() {
return __CLASS__;
}
}
abstract class Model extends Entity {
public function __toString() {
return __CLASS__;
}
}
$cp = new ConcreteProvider();
var_dump($cp); // Outputs object
// Force cast of $cp object to a string, which calls __toString()
var_dump((string) $cp); // Outputs Provider
// Vanilla echo forces object cast to string
echo $cp; //Outputs Provider
$cm = new ConcreteModel();
var_dump($cm); // Outputs object
// Force cast of $cp object to a string, which calls __toString()
var_dump((string) $cm); // Outputs Model
// Vanilla echo forces object cast to string
echo $cm;
With this approach you don't need to force a getId method upon child classes extending the parent Entity class.
Also, Using a getId() for this functionality is mis-leading imo, 99% of the time when I call on a classe's getId() method, I expect an int representing a specific instance of an object, fetched from a store (database, cache, flat-file).

Update 2014:
If you are running PHP 5.4+ you could use a trait to share this function between Models and Providers.
What is to stop you from using the type system? Where you might currently call:
if ($object->getId() == 'Provider') {
// provider specific code
}
Can't you use:
if ($object instanceof Provider) {
// provider specific code
}
Admittedly using type conditionals like this defeats the purpose of polymorphism. I might be able to offer better advice with more context.

Related

Abstract class or interface where declaration method signature allows type hinting of child classes

TLDR: The add method with a child object argument doesn't match the implementation which calls the parent class. Is there a way around this?
Unless there is a better way, my plan was to create an interface for Collections, like so:
interface Collection {
public function add( ValueObjects $obj ) : bool;
}
abstract class ValueObjects {}
class User extends ValueObjects {
}
I am then implementing the Collection interface on concrete collection of User value objects.
class UserCollection implements Collection {
public function add( User $user ) : bool
{
return true;
}
}
This obviously throws an error that Declaration should be compatible with Collection. Is there a way to achieve the same result to allow children objects in the signature? I tried with abstract classes and no result.
It would be even better if I didn't have to extend the abstract ValueObjects class.
Thanks for sharing your knowledge SO community.
PS... reviewed this question , decorator and composition patterns. I either missed it or my question is a little different.
The short answer is "No" because of the lack of generic classes in PHP.
To work around this, you should keep using the abstract type (here ValueObjects) in the implement class and then check for the actual type by yourself.
class UserCollection implements Collection {
public function add( ValueObjects $obj ) : bool
{
if (!($obj instanceof User)) {
throw new RuntimeException('...');
}
/** #var User $obj */
// The above comment make your IDE know the $obj is a `User` instance
// your actual logic
return true;
}
}
One small note, you do not need to cast the $obj object from ValueObjects to User in PHP. A phpDoc inline #var comment line as in the code above only tell the IDE that the $obj is a User instance and support auto-completing for User methods. Without it, the PHP script still run.
I would argue for something along these lines. Note how I'm extending core concepts of objectively collecting, while behaving as a self-validating subject. Then I compose these various constructs into a knowable, concrete composite.
interface Transportable {};
interface User {};
interface Collected
{
public function isValid($item): bool;
// Contract, add is deterministic, require no return
public function add(Transportable $item): void;
}
trait SanityChecked
{
public function isValid($item): bool
{
return true;
}
}
abstract class Collector implements Collected
{
use SanityChecked;
private $items = [];
public function add(Transportable $item): void
{
if ($this->isValid($item) && $this->items[] = $item) {
return;
}
throw new Exception('This is the not the droid we were looking for.');
}
}
class Users extends Collector
{
public function isValid($test): bool
{
return $test instanceof User;
}
}
Which can be mocked as:
$users = new Users();
$users->add(new class() implements Transportable, User {});
echo 'Added first!'.PHP_EOL;
$users->add(new class() implements User {}); // Sorry, error.
echo 'Added second!'.PHP_EOL;
https://3v4l.org/O2qfJ
Another way of looking at it is to further extend behavior of trait:
trait ValidatesAsUser
{
public function isValid(Transportable $user): bool
{
return $user instanceof User;
}
}
class PortalUsers extends Collector
{
use ValidatesAsUser;
}
class ContentEditors extends PortalUsers {}
class Authors extends ContentEditors {}
class AuthorsWithPublishedStoriesByRating extends Authors {}
I think the last part about projections is particularly interesting.

Factory Method: Prevent a class from Direct Instancing

I have a Factory Method to instance a class. Is there a way to prevent this class from direct instancing?
The only option I see is to use an argument passed into the __construct(), but that's not something I'm looking for.
On the other hand, making the __construct() private would be ideal, but I don't want MyClass to extend the Factory without actual need.
What do you guys think?
Factory Method:
class Factory
{
public static function instance()
{
return new MyClass(true);
}
}
MyClass:
class MyClass
{
public function __construct($isFactory = false)
{
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
}
There are hacks to do that:
abusing inheritance to use a protected constructor
putting the factory method inside the class so that it can call the private constructor, which is actually not a hack. But then why not using the constructor in the first place?
using reflection to access the private constructor
I'm not promoting anything of that. What I personally do is documenting the API with things like #internal and leave it to the client following that contract.
In essence, your code should have read something like this:
THE FACTORY
<?php
class Factory {
public static function instance(){
return new MyClass(true); //HERE YOU ARE INSTANTIATING
}
}
THE CLASS TO BE INSTANTIATED VIA THE FACTORY
<?php
//NOT MyClass() <--- YOU ARE DEFINING.... NOT INSTANTIATING...
class MyClass {
public function __construct($isFactory = false) {
if (!$isFactory) {
throw new Exception('Use Factory::instance() to create an object');
}
}
//...MORE METHODS
}
Could you try this instead?
<?php
class Factory
{
private static $FACTORY_GUARANTOR; //ONLY SET DURING INSTANTIATION
public static function instance($type) {
if (class_exists($type)) {
self::$FACTORY_GUARANTOR = 1;
$instance = new $type();
self::$FACTORY_GUARANTOR = null;
return $instance;
}
else {
throw new Exception("Class not found...");
}
}
//YOU CAN GET $FACTORYGUARANTOR EXTERNALLY BUT NEVER SET IT;
public static function getGuarantor(){
return self::$FACTORY_GUARANTOR;
}
}
class MyClass {
protected $property1;
protected $property3;
protected $property2;
public function __construct() {
// IF SOMEONE TRIES TO INSTANTIATE THE CLASS OUTSIDE OF THE FACTORY... BLOW A WHISTLE
if(!Factory::getGuarantor()){
throw new Exception('Use Factory::instance() to create an object');
}
// IF THE PROGRAM MADE IT TO THIS POINT;
// JUST INSTANTIATE THE CLASS BECAUSE MOST LIKELY IT IS COMING FROM THE FACTORY
var_dump($this); // A LITTLE CONFIRMATION....
}
//...MORE METHODS
}
// TRY IT OUT:
/*INSTANCE A: RIGHT*/ $theClass = Factory::instance("MyClass"); //INSTANTIATES THE CLASS
/*INSTANCE B: WRONG*/ $theClass = new MyClass(); //THROWS AN EXCEPTION
The easiest way is to define your base class as abstract. The abstract classes cannot be directly instanced, so you will have to redefine their abstract members in the inherited classes:
abstract class Factory
{
abstract public function foo();
}
class InheritedClass extends Factory
{
public function foo()
{
// Do something
}
}
// $obj1 = new Factory(); // Will produce an error
$obj1 = new InheritedClass(); // Will be executed successfully
You can read more for the abstract classes here: PHP: Class Abstraction - Manual.
For me, the best way is to use ReflectionClass:
class MyClass
{
public const FRIEND_CLASSES = [Factory::class];
protected function __construct() {}
}
trait Constructor
{
protected function createObject(string $className, array $args = [])
{
if (!in_array(static::class, $className::FRIEND_CLASSES)) {
throw new \Exception("Call to private or protected {$className}::__construct() from invalid context");
}
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
$constructor->setAccessible(true);
$object = $reflection->newInstanceWithoutConstructor();
$constructor->invokeArgs($object, $args);
return $object;
}
}
class Factory
{
use Constructor;
public function MyClass(): MyClass
{
return $this->createObject(MyClass::class);
}
}
In constant FRIEND_CLASSES you can define in which classes the class can be instanced.
trait is used because this functionality can be used in different factories that are not related.
If you need to put parameters into constructor of the class, put them as second parameter of createObject.
Details I described in the article "Forbidding of creating objects outside factory in PHP"

Redefining PHP class functions on the fly?

I am trying to figure out how to import a large number of PHP class functions on the fly. For example...
class Entity
{
public function __construct($type)
{
require_once $type."_functions.php"
}
// ...
}
$person = new Entity("human");
$person->sayhi();
$cow = new Entity("cow");
$cow->sayhi();
human_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Hello world!";
}
}
cow_functions.php:
class Entity redefines Entity
{
public function sayhi()
{
echo "Moo!";
}
}
I have found a few possibilities like classkit_method_redefine() and runkit_method_redefine() (which are "experimental", and they cannot modify the currently running class anyway). I am on PHP 5.3.3 right now, so I can't use Traits (Not sure if that is what I am looking for anyways). I have had success redefining the handler variable like this:
// Example 2:
class OtherEntity { /* Code Here */ }
class Entity
{
public function __construct($type)
{
global $foo;
unset($foo);
$foo = new OtherEntity();
}
}
$foo = new Entity();
But, this feels like a very hacky method. More importantly, if I don't name every instance of the class $foo, then it will not work. Are there any workarounds for what I am trying to do?
Note: I am aware that I can extend a class, but in my case when the Entity class is initiated, there is no safe way to know in advance what subclass it would need to be initiated with. Perhaps there is a method I could write, such as:
public function changeClass
{
this->class = OtherEntity;
}
Thanks for your help!
Here's an idea of a possible solution you could try. Let the Cow and Human classes extend the Entity class. However, the Entity class would use a factory to instantiate the objects based on if the value was safe. Let's look at this in more detail:
/*
* Class Entity should not be able to be instantiated.
* It should contain a factory to instantiate the
* appropriate entity and an abstract function declaring
* the method that each entity will need to implement.
*/
abstract class Entity {
public static function factory($type) {
return (is_subclass_of($type, "Entity")) ? new $type() : FALSE;
}
abstract public function sayHi();
}
/*
* Human class extends Entity and implements the
* abstract method from Entity.
*/
class Human extends Entity {
public function sayHi() {
echo "Hello World!";
}
}
/*
* Cow class extends Entity and implements the
* abstract method from Entity.
*/
class Cow extends Entity {
public function sayHi() {
echo "Moo!";
}
}
Now to use this method, call the factory method and if all works well, it'll instantiate the proper class which will extend Entity.
$person = Entity::factory("Human");
$person->sayHi();
$cow = Entity::factory("Cow");
$cow->sayHi();
Using, is_subclass_of() will keep you safe because if the passed in value is not a class that extends Entity, you'll be returned a value of FALSE.
If you'd like to see the above code in action, copy the above php code and test it out on phpfiddle.org.
One thing you can do is create Human and Cow as subclasses of Entity. When you do new Entity("Human"), you can store a newly created Human object inside the Entity instance.
Then you can use __call to redirect method calls to the "child element".
class Entity{
private $child;
public function __construct($type){
$this->child = new $type;
}
public function __call($func, $params=array()){
$method = method_exists($this, $func)
? [$this, $func] : [$this->child, $func];
return call_user_func_array($method, $params);
}
}
class Human extends Entity{
public function __construct(){}
public function sayhi(){
echo "Hello world!";
}
}
class Cow extends Entity{
public function __construct(){}
public function sayhi(){
echo "Moo!";
}
}
$person = new Entity("Human");
$person->sayhi();
$cow = new Entity("Cow");
$cow->sayhi();
The only downside is that $person and $cow are both Entity objects.

What's the best way to implement a factory method for an extended class?

Consider the following code:
class Vehicle {
/**
* Create a new instance of Vehicle
*
* #return Vehicle
*/
public static function create(){
return eval( "return new " . get_called_class() . '();' );
// return self(); would always return Vehicle reg ardless
}
public function drive(){
echo "I am a Vehicle!";
}
}
class Bus extends Vehicle {
public function drive(){
parent::drive();
echo "\nSpecifically, a bus!";
}
}
class Car extends Vehicle {
public function drive(){
parent::drive();
echo "\nSpecifically, a car!";
}
}
// Drive a car
Car::create()->drive();
// Drive a bus
Bus::create()->drive();
I've implemented a factory "create" method in the Vehicle class that allows me to get an instance of the class that I want to use.
I tried using "return new self();" but that always returns an instance of Vehicle, so I resorted to using eval.
question: Is there a non-eval way to implement the create() method so that:
it returns an instance of the class you're using
it doesn't require implementing create() on each of the extending classes
Use static instead of self, e.g.
<?php
class Vehicle {
public static function create(){
return new static();
}
public function drive(){
echo "I am a Vehicle!";
}
}
class Bus extends Vehicle {
public function drive(){
parent::drive();
echo "\nSpecifically, a bus!";
}
}
$b = Bus::create();
$b->drive();
prints
I am a Vehicle!
Specifically, a bus!
(VolkerK beat me, but this has a slight variation)
Wait, why do you need to eval() at all? Wouldn't:
public static function create() {
$class = get_called_class();
return new $class();
}
work?
The best way is to move the factory method out of the concrete type and into a factory class of it's own. You can then not only handle this more easily but you can also replace the factory with another factory easily.
I assume you know how inheritance with objects work, so you don't have to deal with anything static which is less straight forward and starts to stand in someones way pretty fast.

PHPUnit: Use Mock to modify return value?

I would like to have a PHPUnit Mock which executes a method like normal, but then modifies the return value in some way before the function returns.
What I have
I have a set of derived classes, similar to below:
abstract class Base
{
abstract protected function getUrl();
public function callUrl() {
$url = $this->getUrl();
// some code to call the URL here
}
}
class Foo extends Base
{
protected function getUrl() {
return "http://www.example.com/Foo";
}
}
class Bar extends Base
{
protected function getUrl() {
return "http://www.example.com/Bar";
}
}
Please note the classes I have are much more complex, and some of the items I have to test have side-effects (such as writing to a database, etc).
The naive, duplicate code approach
If I only had a single derived class (eg; Foo), then I could do the following:
class FooMock extends Foo
{
protected function getUrl() {
return parent::getUrl() . "?sandbox";
}
}
class theTest extends PHPUnit_Framework_TestCase
{
public function testIt() {
$mock = new FooMock();
// assert something
}
}
Unfortunately, this means I would need a specific "Mock" class for each derived class I want to test, all of which perform exactly the same function.
The preferred approach
Instead, I would like to be able to do something like the following:
function callback ($returnValue) {
return $returnValue . "?sandbox";
}
class theTest extends PHPUnit_Framework_TestCase
{
private $mock;
public function testFoo() {
$this->mock = $this->getMockBuilder('Foo')->getMock();
$this->setupMock();
// assert something
}
public function testBar() {
$this->mock = $this->getMockBuilder('Bar')->getMock();
$this->setupMock();
// assert something
}
public function setupMock() {
$this->mock->expects($this->any())
->method('getUrl')
->will($this->postProcessReturnValue('callback'));
}
}
Is this at all possible with PHPUnit?
Update: It was suggested I have an instance of the original class, and an instance of the mock class. Use the original class to get the original return value and modify that. This modified value is then used as the return for the Mock. This is not a feasible way to go about things as the classes are more complex (they have side effects such as writing to the DB).
An example where this would not work;
class Foo extends Base
{
$id = 0;
public function saveToDB() {
$this->id = saveToDBAndReturnId();
}
protected function getUrl() {
if ($this->id > 0) {
return "http://www.example.com/".$this->id;
}
throw new Exception("No ID");
}
}
$foo = new Foo();
$foo->saveToDB();
$url = $foo->getUrl();
Obviously the returned URL would be different between multiple calls. I could always mock saveToDB, but that's starting to feel dirty when all I want to do is post-process the result of getUrl.
PHPUnit allows you to define a stub method that will use a callback to determine what to return.
$this->mock->expects($this->any())
->method('getUrl')
->will($this->returnCallback('callback'));
You can define your callback to call the original class method and modify the return value.
Of course, using mock objects in this way more or less defeats the purpose of having them be "mock" objects, since the mock objects will now rely on the underlying implementation.

Categories