I have assign subjects to certain group, there's multiple algorithms to do this but they need various of method parameters to calculate output.
For example we can have
interface Algorithm {
randomize(???) : int
}
class A implements Algorithm {
randomize(int, int) : int
}
class B implements Algorithm {
randomize(int, int, string) : int
}
The idea is to add another method for the Algorithm interface, setup() and create structure like this
abstract class Configurer {
private __constructor;
// for which subject we need to take info. from db
init(int $subjectId) : Configurer
setup(Algorithm) : void;
}
interface Algorithm {
randomize() : int
setup(Configurer) : void
isConfigured() : bool
}
It solves problem of parameters which are stored in db but i still don't know how to handle additional parameters passed by users in form. Should i pass also form parameters as array to Configurer::init ?
You can have variable amount of parameters passed in:
interface Algorithm {
public function randomize(mixed ...$params): int;
}
then when using it:
$a = new A();
// Call in different ways:
$a->randomize(someParameter: 1, anotherParameter: 2);
$a->randomize(...['someParameter' => 1, 'anotherParameter' => 2]);
$a->randomize(1, 2);
Related
I'm using Laravel 9 and I have a request can contains :
Parameter called SEASON the value can be an array or null
so SEASON parameter can be an array and can be also null
Parameter called EXPIRY can be an array and can be also null
I have two classes one for the SEASON feature and the other class for EXPIRY both they extends from Repository. and both have a method called execute that return an array
abstract class Repository
{
abstract public function execute(): array;
}
class Expiry extends Repository
{
public function execute()
{
return ['The Request contain Expiry Parameter, and seasonal behaviours is done'];
}
}
class Season extends Repository
{
public function execute()
{
return ['The Request contain Season Parameter, and expiry behaviours is done'];
}
}
I would like to call execute method of Season class if my request contains SEASON, or call the execute method of expiry if my request contains Expiry. OR Call both of them and merge the execute return of execute in one array so I can have as result.
['The Request contain Expiry Parameter, and seasonal behaviours is done', 'The Request contain Expiry Parameter, and expiry behaviours is done']
That's what I tried inside my controller :
public function bootstrap($data)
{
$parseTopics = Helper::parseTopicsRequest();
$basicProgram = new BasicProgramRepository();
$seasonalProgram = new SeasonalProgramRepository($parseTopics['SEASONAL']);
$object = count($parseTopics['SEASONAL']) ? $seasonalProgram : $basicProgram;
// Polymorphism
return $object->execute();
}
Question 1 :
I'm not sure if I should use this way or something like to fix my need:
$employe = new Program(new BasicProgramRepository());
Expected Result :
The expected result depends on if I have season parameter and expiry. What I want to achieve is to use different behaviours ( execute method )
if you want to achieve Polymorphism method, it will be better creating repository or something only for managing that logic.
here is sample.
class SampleRepository
{
/**
* repository instance value
*
* #var string[] | null
*/
private $sampleArray; // maybe here is SEASON or EXPIRY or null
/**
* constructor
*
* #param string[] | null $sampleArray
*/
public function __construct($sampleArray)
{
$this->sampleArray = $sampleArray;
}
/**
* execute like class interface role
*
* #return array
*/
public function execute()
{
return (!$this->sampleArray) ? [] : $this->getResult();
}
/**
* get result
*
* #return array
*/
private function getResult()
{
// maybe pattern will be better to manage another class or trait.
$pattern = [
"SEASON" => new Season(),
"EXPIRY" => new Expiry()
];
return collect($this->sampleArray)->map(function($itemKey){
$requestClass = data_get($pattern,$itemKey);
if (!$requestClass){ // here is space you don't expect class or canIt find correct class
return ["something wrong"];
}
return $requestClass->execute();
})->flatten();
}
}
and you can call like this.
$sampleRepository = new SampleRepository($sampleValue); // expect string[] or null like ["SEASON"],["SEASON","EXPIRY"],null
$result = $sampleRepository->execute(); // [string] or [string,string] or []
this approach is only what your parameter is secified value.
if your return result is almost same both of Season class and Expiry class, it will be better to manage on trait. (that is $pattern on sample code)
try some.
I read comments,so following..
For example, it prefers to be only getting result of getResult().
so, some pattern and so many logics shouldn't be written on getResult();
If you use trait, this is sample.
first, you need to create managing behaviors class.
Behavior.php
<?php
namespace App\Repositories;
class Behavior
{
use Behavior\BehaviorTrait;
// if you need to add another pattern, you can add trait here.
}
and then, you need to create Behavior directory at same level place.
you move that directory, you create trait file like this.
<?php
namespace App\Repositories\Behavior;
trait BehaviorTrait
{
public static function findAccessibleClass(string $itemKey)
{
return data_get([
"SEASON" => new Season(),
"EXPIRY" => new Expiry()
],$itemKey);
}
}
findAccessibleClass() method has responsible of finding correct class.
then, you call this method like this.
private function getResult()
{
return collect($this->sampleArray)->map(function($itemKey){
$requestClass = Behavior::findAccessibleClass($itemKey); // fix here.
if (!$requestClass){ // here is space you don't expect class or canIt find correct class
return ["something wrong"];
}
return $requestClass->execute();
})->flatten();
}
if your code is so much in getResult(), you will be better to separate code for responsible.
To create Behavior trait, getResult don't need to have responsible of behavior logic. it will be easy testing or fixable in short.
hope well.
I have a constructor that asks for a type of class, but it doesn't define that as a type hint. You are able to pass anything you want to it, and it will accept it. Is there a way to pass a class type to the constructor, and in the add() method it only accepts that type?
Currently what I have, is the ability to pass anything to the constructor such as an int, string, bool, etc. Is there a way to make it so that the constructor only accepts class types?
class Main{
protected $items = [];
protected $type = '';
public function __construct($type){
$this->type = $type;
}
public function add($object){
if($object instanceof $this->type){
$this->items[] = $object;
}
}
}
class Test{}
class Awesome{}
$main1 = new Main(Test::class);
$main2 = new Main(Awesome::class);
// Successful:
$main1->add(new Test());
// Fail:
$main1->add(new Awesome());
// Successful:
$main2->add(new Awesome());
// Fail:
$main2->add(new Test());
If I were to do it in C# it would look something like this:
Main main1 = new Main<Test>();
Main main2 = new Main<Awesome>();
Basically it says that add() will only allow instances of Test. Is there a way to do some
Php doesn't support template like declarations like e.g. c++.
The best way you may be able to achive this is by passing a lambda which then in return gets used in order to validate the passed parameter in add.
<?php
class Test {
private $validator = null;
public function __construct($validator) {
$this->validator = $validator;
}
public function add($value) {
$func = $this->validator;
$validated = $func($value);
echo $validated ? 'OK' : 'NG';
}
}
$obj = new Test(function($value) {
return is_int($value);
});
$obj->add(11);
$obj->add('string');
Another possibility would be to pass the type e.g. "ClassName" in your constructor and use get_class() and gettype() for the validation.
In the future there may be smarter solutions since you'll be able to write anonymous classes but I haven't really thought about that but in the end they would work similarly to lambdas.
Basically it says that add() will only allow instances of Test.
It's possible to achieve this in PHP by simply adding the type before the argument name in the function definition (similar with C/C++/C# types):
class Main {
protected $items = [];
public function add(Test $object) {
$this->items[] = $object;
}
}
PHP 5 accepts classes, interfaces, array and callable as type hints. If Test is a class then Main::add() accepts objects of class Test and its children. If Test is an interface, then the method Main::add() accepts objects that implement Test or one of its children.
PHP 7 (coming soon to a server near you) introduces type hinting for scalar types too.
PHP does not support anything similar with C++ templates or C# generics. If you want to create a class that works with objects of type A and another class that has identical behaviour but works with objects of type B you have several options but none of them is as elegant as the templates/generics:
Create two classes having identical behaviour, one for objects of type A, another for objects of type B; use different type hints (A and B) in the arguments lists of the methods of the two classes to enforce the separation - not scalable;
Something similar to your code, use the allowed class name as a string property and check it on any operation; you can also validate the argument of the constructor using class_exists() - the code becomes cluttered with tests and less readable;
Use OOP polymorphism; extend both A and B from the same class T or, even better, make A and B implement the same interface I. A PHP interface can be empty, it doesn't need to declare anything; empty interfaces used just for type hinting are common practice in PHP.
Then write a single class Main and use I as type hint for all its methods that accept objects. It will accept objects of both types A and B but if you also declare functions in I (and implement them in A and B, of course) then use them in Main you can be sure nothing breaks (I becomes a contract between Main and the objects its accepts as arguments for its methods).
I would choose option #3 because it gets the most help from the interpreter; it verifies the type of the arguments on each function call that has type hints and triggers a recoverable fatal error (in PHP 5) or throws an exception (in PHP 7).
Also some IDEs and static code analysis tools can validate the calls without running the code and help you fix it.
Is there a way to make it so that the constructor only accepts class
types?
Nope!
It is not possible in PHP. Not like C#, at least.
You need either set a type hint or set any types.
However, there's a closer solution in order to accept only class when instancing a class: Using ReflectionClass!
class Main {
protected $items = [];
protected $type = null;
public function __construct($type) {
$reflector = new ReflectionClass($type);
$this->type = $reflector->getName(); # or: $this->type = $type;
}
public function add($object) {
if($object instanceof $this->type) {
$this->items[] = $object;
}
}
}
As ReflectionClass contructor argument only accpets a string containing the name of the class to reflect, you can take advantage that, so passing scalars strings will cause an exception.
$main = new Main(Test::class); # Okay!
$main = new Main('Test'); # Okay!
However
$main = new Main('bool');
// Results
# PHP Fatal error: Uncaught exception 'ReflectionException'
# with message 'Class bool does not exist' in ...
Change your constructor to this:
public function __construct(Type $type){
$this->type = $type;
}
This is based on the assumption that $type is an instance of Type.
Mockery has a method hasKey(), which checks if a given parameter has a certain key. I want to make sure that the passed array has multiple keys. I would also like to assert that the array has x amount of elements.
Is there a built-in way to allow for custom parameter expectations? I tried using a closure that returns true or false based on the given parameter, but that didn't work.
Thanks.
Edit:
Example
$obj = m::mock('MyClass');
$obj->shouldReceive('method')->once()->with(m::hasKey('mykeyname'));
What I'm trying to do is to have more insight into what is passed to the method using with(). I want to assert that an array passed to a method has both key a AND key b. It would be great if I could use a closure somehow to create my own assertion, such as counting the number of array elements.
You can user a custom matcher.
Out of the top of my head (not tested) this could look something like this:
class HasKeysMatcher extends \Mockery\Matcher\MatcherAbstract
{
protected $expectedNumberOfElements;
public function __construct($expectedKeys, $expectedNumberOfElements)
{
parent::__construct($expectedKeys);
$this->expectedNumberOfElements =$expectedNumberOfElements;
}
public function match(&$actual)
{
foreach($this->_expected as $expectedKey){
if (!array_key_exists($expectedKey, $actual)){
return false;
}
}
return $this->expectedNumberOfElements==count($actual);
}
/**
* Return a string representation of this Matcher
*
* #return string
*/
public function __toString()
{
return '<HasKeys>';
}
}
and then use it like this:
$obj = m::mock('MyClass');
$obj->shouldReceive('method')->once()->with(new HasKeysMatcher(array('key1','key2'),5));
I have the following PHP functions:
public function foo($a, $b, $c){
q = xyz($a,$b,$c);
return q;
}
public function foo($a, $b ){
q = xyz($a,$b);
return q;
}
I want to create a private function that evaluates if param $c is there it uses it, if its not; it does not.
So that what I eventually will do is call the private function to evaluate the params and then run query.
It is hard to know exactly what you want because there is not a lot of a context in your question, but this seems straightforward enough to me - public method foo() accepts either 2 or 3 values, and then calls private method xyz(), which also accepts 2 or 3 values.
In this case you can make the third argument $c optional in each method by assigning a default value (null in the example below, but you can define a string, int, array etc. Whatever suits your scenario).
Presumably you are applying some functionality to the values passed into foo() before calling xyz() because otherwise I cannot see any reason for implementing that method.
Finally, you can implement code checking and using the value of $c in xyz().
An example in the context of a class called Bar below:
class Bar
{
public function foo($a, $b, $c = null)
{
// do something here, e.g: input validation
// before calling private method Foo::xyz()
return $this->xyz($a, $b, $c);
}
private function xyz($a, $b, $c = null)
{
// handle variables... isset($c) etc.
// implement functionality
return '';
}
}
Hope this helps :)
EDIT I see Alma Do suggested same in the question comments.
EDIT Actually in this case it's worth pointing out the default value for $c in the method signature for xyz($a, $b, $c = null) is redundant, since it will always have some value when called through foo() (either an explicit value or null). However, if you are calling xyz() internally on other parts of the method, with 2 or 3 values explicitly in different cases... you may very well want to do this so it's worth keeping the default value for flexibility.
The ultimate goal being, let xyz() itself decide if it wants to use the value of $c or not.
You can define a default value in the function definition.
With that, the function can be called with or without that parameter and you can ask for the default to distinguish between the cases.
Choose a default that is least likely to be actually chosen as a value for the parameter.
<?php
public function foo($a, $b, $c = null){
if(is_null($c) {
$q = doThis($a,$b);
}
else {
$q = doThat($a,$b,$c);
}
return $q;
}
look this,may be help you !
public function foo(){
return call_user_func_array("xyz",func_get_args());
}
Should interfaces in PHP return the same types ?
My friend told me that it should return the same types.(for good practice)
I know that PHP is dynamic type language and it isn't possible.
For example:
interface idReturn
{
//Return INT
public function getById($id);
}
interface arrayReturn
{
//Return array
public function getByData($data);
}
Is it good practice or not?
It's not an interface thing. To avoid type checking in the consumers, a method should return one type only. It makes for easier to understand code due to the reduced number of execution pathes through the code.
For instance, this adds unnecessary complexity:
function get_users()
{
// some code returning array when users have been found
// or null when no users have been found
}
$users = get_users();
if (!is_null($users)) {
foreach ($users as $user) {
// do something with $user
}
}
If get_users() would simply return an empty array when no users have been found, you can simplify the consuming code to just read
foreach (get_users() as $user) {
// do something with $user
}
Also, by virtue of Liskov's Substitution Principle, any classes that are subtypes of a supertype need to be usable interchangeably in a consumer using the supertype. If the consumer expects an integer, you may not return a string since that might break the consumer. The same holds true for classes implementing an interface and consumers of that interface, e.g.
interface Contract
{
/** #return array */
public function fn();
}
Now assume A implements Contract and returns array, while B implements Contract and returns array or null. My consumer expecting an array will now break when B returns null:
function fn(Contract $instance)
{
// will break when B returns null
foreach ($instance->fn() as $foo) {
// do something with $foo
}
}
Since PHP cannot currently enforce a return type, it's up to the developer to indicate it in the DocBlock and to make sure the implementing or subtyped classes adhere to that return type.
With that said, if the contract defines multiple possible return types, the consumer has to make sure it can handle them. However, that again means undesirable type checking.
He probably meant this:
interface I {
public function f();
}
class A implements I {
public function f() {
return 1; // int
}
}
class B implements I {
public function f() {
return 'a'; // string
}
}
Whether or not you want this depends entirely on the contract you define for I::f. Contracts should be put in separate documentation because PHP lacks support for formally specifying them (this is not PHP-specific and it is the case in most languages, notable exceptions being D and Eiffel).