How to implmenet this mechanism using Abstract Class in PHP? - php

From the past two days i have been diving into the concepts of OOPS in PHP, and i found Abstract Class to be very useful concept and i wanted to implement it in my application. and here is why i want to implement it.
My Application Consist of Several Unstructured Pattern which uses several different classes without any hierarchies, due to this i have to use several repeated codes. i want to cut this all up and structure it properly, basically what i want to do is
Define a parent class which is not instantiable from outside.
Define all properties in this parent class so that i can re-use the same property for different child classes.
Define re-usable class methods and objects so that child class can use it without the need of defining it again and again (Inheritance).
Basically My Abstract class should be capable of inheriting the re-usable methods and properties and at the same time it should act as an Interface.
To demonstrate you clearly let me show you some sample codes which i have been using.
public $error = array(); //used to handle error mechanism and to hold the errors.
private $data = array(); //used with Accessor Methods (__set and __get)
private $validate; //holds Validate Object
private $dbh; //holds Database Object
public function __set($property, $value) {
if( in_array($property, $this->data)) {
return $this->data[$property] = $value;
} else {
return false;
}
}
public function __get($property) {
return 'Access Denied to Class Property [ '.$property.' ]';
}
Above codes are repeated for almost every class, this is the reason i want to define it once in a parent class and control the mechanism from there.
As i am still Novice to Many OOPs concept i am unable to understand how do i achieve what i want using Abstract Class. below is the sample code i tried using which ofcourse is wrong for declaring an abstract method.
abstract class Property {
protected $error = array();
protected $data = array();
protected $dbh;
protected $validate;
abstract protected function connectDB($dbhandle) {
return $this->dbh = $dbhandle;
}
abstract protected function setValObj($valObj) {
return $this->validate = $valObj;
}
public function __set($property, $value) {
}
public function __get($property) {
}
}
here is what i want to do.
When a child class is initiated it should be forced to define methods as declared in abstract class.
A Child class should only be able to call and pass the arguement but not extend an abstract method. the mechanism should be handled by parent class. this is what i tried to do in my code.
i know i am missing something, or might be i have not got the concept right, could somebody explain me what exactly should i be doing to achieve the same result.

1 . Define a parent class which is not instantiable from outside.
All abstract classes can not be instantiated, only extended. So this is what you already have:
abstract class Property {
On to the next:
2 . Define all properties in this parent class so that i can re-use the same property for different child classes.
Just write them into that abstract class (often called base class or abstract base class or template class as well). All classes extending from a base class, will have access to protected or public member methods and properties. If you make a variable or function private, it's only available to code in the base class.
So just define all you want to have in the base class to be shared amongst all extending classes and you only need to type it once.
3 . Define re-usable class methods and objects so that child class can use it without the need of calling it again and again (Inheritance).
This works automatically. As you extend from the base class, the public and protected class methods defined therein are automatically accessible through the extending class. You do not (but you can unless specified with final) need to add that function again to make it available. E.g. all public methods from the base class are automatically available publicly on all classes that extend from it.
Then you continue:
here is what i want to do.
1 . When a child class is initiated it should be forced to define methods as declared in abstract class.
You can do so by defining these needed methods as abstract. Abstract methods needs to be implemented by the extending class. However you can not put code into abstract methods in the base class. That's left there for the extending class.
2 . A Child class should only be able to call and pass the arguement but not extend an abstract method. the mechanism should be handled by parent class. this is what i tried to do in my code.
If you want to prevent a subclass to overwrite a function, declare it as final in your base class. Final methods can not be further extended.
But probably you want to do something that is technically not possible, e.g. prevent that a method can be extended while you require that is should be extended.
In your code you're using magic functions to access the properties / values. Those don't count in the sense that their name changes. So you loose the control of the inheritance for a bigger part of your class design.
However, you can implement array access to offer getter/setters. It's bound to a concrete interface and you then can disallow access through the base class and prevent extending of this area for classes that will extend from it.
Let me know if you would like to have some example code, probably SPL is new to you as well.
Provide variable inheritance via ArrayAccess
As you've been running into the problem that inheritance can not be easily used on the magic function __get() and __set() which are available, I had the idea to make that part of the access concrete that does not change (get, set) while it's still possible to name the property variable. An interface that is available with PHP that already does this is ArrayAccess. It was designed to give access to properties via the style we know from standard php arrays ([]) and it's normally used for that. But for this example it has the benefit to already provide an interface as well that fits the general need.
First a demonstration how such a class behaves in use:
# give birth to the object
$object = new PropertyClass; // one of your property classes
# get:
$value = $object['propertyA'];
# set:
$object['propertyA'] = 'new value';
# unset:
unset($object['propertyA']); // and gone ;)
# isset:
isset($object['propertyA']); // true / false
Okay, as this shows, this looks like an array, but is an object. The rest of $object works as known, so this is no limitation, but an addition.
As you can imagine already with this code, there must be a get and set routine as well for reading and setting the properties values like with __get() and __set(). Additionally there must be something for isset and unset, so four. This is the interface definition of ArrayAccess:
ArrayAccess {
/* Methods */
abstract public boolean offsetExists ( mixed $offset )
abstract public mixed offsetGet ( mixed $offset )
abstract public void offsetSet ( mixed $offset , mixed $value )
abstract public void offsetUnset ( mixed $offset )
}
You can extend from that in PHP by implementing the interface. That's not extends but implements. This works with every interface in PHP, but this interface is something special as well. It's provided by the SPL/PHP itself and in the moment a class of yours actually implement the functions, the functionality as described in the code above is automatically added to your class.
As those functions are available publicly, you could call them with their name as well naturally.
So actually this interface qualifies for a properties object as you want to build one and will give you an interface you can put your constraints on.
So the only question left is: How can this look like for your properties class?
Implementing ArrayAccess to a variable properties class
<?php
/**
* Property Object base class based on ArrayAccess
*/
abstract class PropertyObject implements ArrayAccess
{
/** Interface Methods */
/**
* implementing classes must return all names of their properties
* in form of an array.
*/
abstract protected function returnNames();
/** class */
/**
* value store
*
* #var array
*/
private $store = array();
/**
*
* By this design, properties can only contain A-Z and a-z.
*
* look like.
*
* #return bool
*/
private function isValidPropertyName($name) {
return ctype_alpha($name);
}
private function checkOffsetArgument($offset) {
if ($this->isValidPropertyName($offset)) return;
throw new InvalidArgumentException(sprintf('"%s" is not a valid property name.', $offset));
}
private function setNames(array $names) {
foreach($names as $name) {
$this->checkOffsetArgument($name);
}
$len = count($names);
$this->store = $len
? array_combine($names, array_fill(0, $len, null))
: array()
;
}
/**
* final constructor to obtain control
*/
final public function __construct() {
$this->setNames($this->returnNames());
}
/**
* ArrayAccess impl.
*
* #return bool
*/
public function offsetExists($offset) {
$this->checkOffsetArgument($offset);
return array_key_exists($offset, $this->store);
}
/**
* ArrayAccess impl.
*
* #return mixed
*/
public function offsetGet ($offset) {
$this->checkOffsetArgument($offset);
return $this->store[$offset];
}
/**
* ArrayAccess impl.
*/
public function offsetSet($offset, $value) {
$this->checkOffsetArgument($offset);
if (!$this->offsetExists($offset)) {
throw new InvalidArgumentException(sprintf('Property "%s" can not be set.', $offset));
}
$this->store[$offset] = $value;
}
/**
* ArrayAccess impl.
*/
public function offsetUnset($offset) {
$this->checkOffsetArgument($offset);
unset($this->store[$offset]);
}
}
/**
* I feel so concrete.
*/
class ConcreteType extends PropertyObject
{
protected function returnNames() {
return array('propertyA');
}
}
$obj = new ConcreteType;
var_dump($obj['propertyA']); # NULL, maybe you need other default values.
$obj['propertyA'] = 'hello';
var_dump($obj['propertyA']); # string(5) "hello"
var_dump(isset($obj['propertyA'])); # bool(true)
// this will trigger an exception
try {
$obj['XProperty'] = 'good night.';
} catch (Exception $e) {
var_dump($e->getMessage()); # string(36) "Property "XProperty" can not be set."
}
// the following might be unwanted but can be prevented in base class:
unset($obj['propertyA']);
var_dump(isset($obj['propertyA'])); # bool(false)

I think you need to implement mixin interface. According to my knowledge PHP does not support this natively. Some PHP frameworks (like Yii framework) implemented it by itself. I found one example here. But I am sure you will be able to find better examples.

Related

PHPUnit - Assert trait method is called

Supposing I have a trait which currently has a method:
trait MyTrait
{
public function traitMethod()
{
return true;
}
}
Now let's say this trait is used by several classes but I don't want to write a unit-test for every class.
Instead I want to write a single unit-test only for the trait:
public function testTraitMethod()
{
$trait = $this->getMockForTrait(MyTrait::class);
$this->assertTrue($trait->traitMethod());
}
But the problem is that a class may actually override trait's method:
class MyClass
{
use MyTrait;
public function traitMethod()
{
return false;
}
}
In this case MyClass is doing something wrong but I'm not aware of it since I'm testing only the trait.
My idea would be write a single unit-test for every class just to check if it's using that trait and it's not overriding that method. If a class needs to override trait's method then it needs a specific unit-test as well.
Currently I'm writing unit-tests for each class that implements my trait but it's of course kind of copy-paste tests everywhere.
So is there a way to test if a class calls it's underlying trait method?
I found a solution using Reflection, I'll post it in case someone needs it because I couldn't find anything related to my problem. Feel free to comment or add different solutions if you want.
So the following test asserts that $serviceClass uses $traitClass and doesn't override methods declared in $traitClass except abstract ones and those which are manually added to $overriddenMethods array.
public function testServiceUsesTrait()
{
$serviceClass = MyClass::class;
$traitClass = MyTrait::class;
$this->assertContains($traitClass, (new \ReflectionClass($serviceClass))->getTraitNames());
$reflectedTrait = new \ReflectionClass($traitClass);
$reflectedTraitFile = $reflectedTrait->getFileName();
/**
* If $serviceClass overrides some trait methods they
* should be inserted into this array to avoid test failure.
* Additional unit-tests should be written for overridden methods.
*/
$overriddenMethods = [];
foreach ($reflectedTrait->getMethods() as $traitMethod) {
if ($traitMethod->isAbstract() || in_array($traitMethod->getName(), $overriddenMethods, true)) {
continue;
}
$classMethod = new \ReflectionMethod($serviceClass, $traitMethod->getName());
$this->assertSame($reflectedTraitFile, $classMethod->getFileName(), sprintf(
'Trait method "%s" is overridden in class "%s" thus it must be excluded from this test.',
$traitMethod->getName(), $serviceClass
));
}
}
I also tried to compare declaring classes using $classMethod->getDeclaringClass() instead of comparing filenames but it didn't work: even if trait method is not overridden in class, getDeclaringClass() always returns the class itself.

PHPDoc, Factory #return type

Let's say I've
an abstract class with with one abstract method.
2 child classes that define that abstract method in their own way.
A factory that returns an instance of one of 2 child classes based on arguments.
Following is the sample code
abstract class Datalist{
abstract public function render($arg1, $arg2);
}
class Datalist_Table{
public function render($arg1, $arg2){
/* do something here */
}
}
class Datalist_List{
public function render($arg1, $arg2){
/* do something here */
}
}
class DatalistFactory{
/**
* usual stuff
*
* #return Datalist
*/
public static function build($args){
$class_name = 'Datalist_' . $args['type'];
return new $class_name($args['m'][0], $args['m'][1]);
}
}
//in some other file
$list = DatalistFactory::build($args);
$list-> ....
My problem
My IDE (PHPStorm) does not hint on the $list->render(). My guess is that because it has been declared as an abstract.
Question
What should I put in front of #return in PHPDoc for DatalistFactory::build() so that the IDE hints on the functions defined in child classes as well.
P.S.
I've tried instructions in following questions with no success
phpDoc notation to specify return type identical to parameter type
PHPDoc: Is is possible to reference the object property descriptions from factory method docblock?
PHPDoc preconditions
PHPDoc for fluent interface in subclass?
You need to open this file in project. I use phpstorm 8.0.3 create new file in project and insert your code. All works fine =)

Is it compulsory to declare abstract class for abstract method?

I have a below program
<?php
abstract class foo
{
abstract public function callme();
public function testing()
{
return $this->callme();
}
}
class bar extends foo
{
public function callme()
{
return "hello";
}
}
$objBar = new bar();
echo $objBar->testing();
?>
I defined abstract class foo. Is it compulsory to write abstract before class ? Because if i removed abstract i am getting fatal error.
Yes, if it contains abstract methods.
By declaring a method as abstract you are saying that in order to use this class, extending classes must implement the abstract method.
Your foo class cannot be instantiated unless callme is implemented, hence it must be declared abstract.
These concepts are perhaps better explained with a real world example than your standard abstract class Vehicle, class Car extends Vehicle tutorials.
Let's say we have a reporting system that does some querying on the database.
We find that all reports must be implemented in a standard way to share code and help with future maintenance.
So we define:
abstract class Report
{
}
For the sake of argument, all of our reports require a database connection.
abstract class Report
{
/** #var PDO */
protected $dbh;
public function __construct (PDO $dbh)
{
$this->dbh = $dbh;
}
/**
* #return array
*/
abstract public function getData();
}
Here we have also decided that all of our reports must implement a public getData method that returns an array.
This means:
We can ensure all our reports have a database connection
We can instantiate and then run each report in the same way
The abstract class definition has enforced the way we consume this code and makes sure that every type of report, regardless of which developer on your team wrote it, conforms to the convention we have decided.
Other code is then able to select a report from user input, run it, and do something with the result of getData (such as writing it to a CSV file) knowing that it will be an array.

Is it possible to define method with different parameters in a PHP interface?

I'm developing a service that is being injected a Logger object but I can have 2 different kind of loggers, I'm planning on having a syslog logger and a queue message system logger. Is this possible?
The idea is having an interface:
interface Loggable
{
public function log() ;
}
and 2 classes that implement that interface:
class Syslogger implements Loggable
{
public function log()
{
...
}
}
class QMSLogger implements Loggable
{
public function log($queueName)
{
...
}
}
The only way I could come with is having an array as a parameter and use it on one class and not using on the other one... but that is a little bit smelly :P
You're asking if it's possible: yes it is, but…
If you implement an interface, you must respect its contract.
interface Loggable
{
public function log();
}
This interface's contract is you can call log() without any parameter.
In order to respect that, you can make the parameter optional:
class QMSLogger implements Loggable
{
public function log($queueName = null)
{
...
}
}
This is perfectly valid PHP and it respects the Liskov Substitution Principle. Of course, you must not use that optional parameter when coding against the interface, else you are obviously breaking the interface. Such parameter can be useful only when you are using the implementation (e.g. in some part of the code which is tightly coupled to the QMSLogger).
However this is probably not the solution to your problem as $queueName seems to be a configuration value and it might be better to pass it in the class' constructor (as explained in the other answer).
As stated in the comments, that's not the same interface. If you cannot generalize the interface across all possible logger implementations, make the configuration differences part of the instance constructor:
class QMSLogger implements Loggable {
protected $queueName;
public function __construct($queueName) {
$this->queueName = $queueName;
}
public function log() {
...
}
}
I came across a similar case where I wanted to create an interface that simply ensure any classes that implemented it would have a method of the same name, but would allow for implementation with different parameters.
/**
* Interface Loggable
* #method log
*/
interface Loggable
{
}
Now the Loggable interface can be implemented with different parameters like so.
class Syslogger implements Loggable
{
public function log($key, $value)
{
...
}
}
You can also pass the parameters as an array , in this way you respect the contract from one hand and also be flexible to insert any values with any amount inside the array , check this out :
abstract class FeaturesAbstract
{
/**
* #param array $paramsArray
*
* #return mixed
*/
abstract public function addExecute($paramsArray);
}
And to actually use this method you could send the parameters like this :
$this->abstract->addExecute(array('paramA' => $paramA, 'paramB' => $paramB));
And then inside the concrete implementation you get the parameters like this :
/**
* #param array $paramsArray
*
* #return void
*/
public function addExecute($paramsArray)
{
$a = $paramsArray['paramA'];
$b = $paramsArray['paramB'];
$c = ...
}
Good luck :)

Inject filter into Zend_View

I wish to set some properties in MyFilter with constructor injection but it seems impossible with Zend_View::addFilter(string $filter_class_name) since it loads a new instance upon usage. MyFilter implements Zend_Filter_Interface.
Can I somehow inject an instance of a filter to an instance of Zend_View?
Closing since it (hopefully) will be pushed into 2.0, see ticket on JIRA.
You may pass object:
$filter = new Your_Filter($params); // implements Zend_Filter_Interface
$view->addFilter($filter);
You may get view instance from viewRenderer, e.g. using staticHelper.
Edit:
The other method may be:
class MyFilterSetup extends MyFilter implements Zend_Filter_Interface
{
public function __construct($params)
{
$this->_params = $params;
parent::__construct();
}
public function filter($string)
{
// .... $this->_params;
}
}
I'm not certain, but I don't think it's possible. Looking at the sourcecode setFilter() and addFilter() only accept the Filter Classname as a string. You cannot set any options, like you can in Zend_Form for instance. What you could do though is:
class MyFilter implements Zend_Filter_Interface
{
protected static $_config;
public static setConfig(array $options)
{
self::_config = $options;
}
// ... do something with the options
}
and then you set the options where needed with MyFilter::setOptions(), so when Zend_View instantiates the Filter instance, it got what it needs to properly run the filter.
You can't in the 1.x branch, ticket is filed:
http://framework.zend.com/issues/browse/ZF-9718
Can't we create a custom view object extending Zend_View that overrides the addFilter() method to accept either a class or an instance. Then override the _filter() method to deal with both types of filters - string and instance - that we have stored.
Why not assign the filter properties to the view, and then either set the properties when the view is set, or access the view directly in your filtering function? e.g.
$view->assign('MyFilterProperty', 'fubar');
and then in your filter class:
public function setView($aView)
{
$this->_property = $aView->MyFilterPropery;
}
It's kludgy, but it should get the job done.

Categories