Pass an array of interfaces into a function? [duplicate] - php

This question already has answers here:
Type hinting - specify an array of objects
(6 answers)
Closed 6 years ago.
In PHP since the interface benefits can used by passing as parameter mentioning the Interface name something like
public function foo (Abc $abc){}
where Abc is an interface.But how do I pass an array of these interfaces?
Please note this not class but interface and only way to get advantage of interface is passing as function with type hinting

In PHP 5.6+ you could do something like this:
function foo(Abc ...$args) {
}
foo(...$arr);
foo() takes a variable amount of arguments of type Abc, and by calling foo(...$arr) you unpack $arr into a list of arguments. If $arr contains anything other than instances of Abc an error will be thrown.
This is a little 'hacky', but it's the only way to get type hinting for an array in PHP, without putting down some extra code.

Unfortunately, you cannot check for two different interfaces at the same time using type hinting in PHP, but you can write a function for this which will check if the object belongs to multiple interfaces, eg -
function belongs_to_Interfaces($obj,array $interfaces)
{
foreach($interfaces as $interface)
{
if(!is_a($obj,$interface))
{
return false;
}
}
return true;
}
You can then use it like this,
public function foo ($abc){
if(!belongs_to_Interfaces($abc, ['interface1', 'interface2'])){
//throw an error or return false
}
}

if you use PHP 5.6+ you can use variadic with decorator pattern:
<?php
interface Rule {
public function isSatisfied();
}
final class ChainRule implements Rule {
private $rules;
public function __construct(Rule ...$rules) {
$this->rules = $rules;
}
public function isSatisfied() {
foreach($this->rules as $rule)
$rule->isSatisfied();
}
}

Related

Is there a way to return both Response and RedirectResponse type of instances in a single function? [duplicate]

I have some methods that can return one of two return types. I'm using a framework utilizing MCV so refactoring these few functions in particular is not appealing.
Is it possible to declare the return type returning one or the other and failing on anything else?
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
As of PHP 8+, you may use union types:
function test(): FailObject|SuccessObject {}
Another way, available in all versions since PHP 4, is for the two objects to share an interface. Example:
interface ReturnInterface {}
class FailObject implements ReturnInterface {}
class SuccessObject implements ReturnInterface {}
function test(): ReturnInterface {}
In this example, ReturnInterface is empty. Its mere presence supports the needed return type declaration.
You could also use a base, possibly abstract, class.
To me, for this use case, interfaces are more clear and more extensible than union types. For example, if I later want a WarnObject I need only to define it as extending ReturnInterface -- rather than going through all signatures and updating them to FailObject|SuccessObject|WarnObject.
As noted by bishop, there is an RFC for adding multiple return types. However, I thought I'd add that as of PHP7.1 you can now specify a nullable return type like this:
function exampleFunction(string $input) : ?int
{
// Do something
}
So this function would take in a string and by adding the question mark before int you are allowing it to return either null or an integer.
Here's a link to the documentation:
http://php.net/manual/en/functions.returning-values.php
And here's a quote from that page explaining the usage:
PHP 7.1 allows for void and null return types by preceding the type declaration with a ? — (e.g. function canReturnNullorString(): ?string)
Also, here's another thread that relates to this: Nullable return types in PHP7
PHP from 7.2 onward supports the object return type
http://php.net/manual/en/migration72.new-features.php
function test(object $obj) : object
// return any type of object ...
Since PHP 8.0 this is possible.
You can now use union types to specify this:
function test(): Success|Failure
{
if ($this->condition === false) {
return new Failure();
}
return new Success();
}
The fact that this is possible does not mean that it is always advisable. In many (probably most) situations, using an interface (e.g. Result, which both Failure and Failure would implement) as advised in a different answer, is still much preferable.
But there are other instances where union types could make sense to an alternative to weak typing. E.g. a method that accepts both string and int, or to describe the return type of a function like stripos(), which returns int|false.
This ins't correct way:
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
Multiple return type is a bad practice. Good practices:
You should define a exception:
class FailObjectException extends \Exception
{
private $exampleExtraInfo;
public function __construct($exampleExtraInfo, $message)
{
parent::__construct($message);
$this->exampleExtraInfo = $exampleExtraInfo;
}
public function exampleExtraInfo(): int
{
return $this->exampleExtraInfo;
}
}
Now, you can define function like:
function test(): SucceedObject
{
if ($this->condition === false) {
throw new FailObjectException(...,...);
}
return SucceedObject;
}
And use this function with try/catch:
try{
$succeedObject = $any->test();
} catch (FailObjectException $exception){
//do something
}

PHP return array if breaking chain in singleton

I've built a singleton class with chaining methods (to be used in a template).
To make chaining work I need to return new static. It allows the next chain to be added. The problem I have is that I don't want to return the static object if there are no more chains.
Example
<?php
class bread {
public static $array;
public static function blueprints() {
static::$array = array('some', 'values');
return new static;
}
public static function fields() {
return static::$array;
}
}
$blueprints = bread::blueprints();
$fields = bread::blueprints()->fields();
print_r($blueprint) // Returns object - FAIL
print_r($fields ) // Returns array - OK
In the example above I want $blueprints to return an array, because there are no more methods chained on it.
How can that be done?
The simple answer is you cannot do what you want.
Method chaining is not a special thing for Php.
For your example
bread::blueprints()->fields();
This is not different than:
$tmp = bread::blueprints();
$tmp->fields();
So because of the Php does not know the context where the result will be used of it cannot change the return type.
Here is another version of this question:
Check if call is method chaining
However, your class can implement ArrayAccess interface.This will allow you to treat the object like an array without casting and you get total control over how the members are used.
You can try this:
$blueprints = (array)bread::blueprints();

Is it possible to specify multiple return types on PHP 7?

I have some methods that can return one of two return types. I'm using a framework utilizing MCV so refactoring these few functions in particular is not appealing.
Is it possible to declare the return type returning one or the other and failing on anything else?
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
As of PHP 8+, you may use union types:
function test(): FailObject|SuccessObject {}
Another way, available in all versions since PHP 4, is for the two objects to share an interface. Example:
interface ReturnInterface {}
class FailObject implements ReturnInterface {}
class SuccessObject implements ReturnInterface {}
function test(): ReturnInterface {}
In this example, ReturnInterface is empty. Its mere presence supports the needed return type declaration.
You could also use a base, possibly abstract, class.
To me, for this use case, interfaces are more clear and more extensible than union types. For example, if I later want a WarnObject I need only to define it as extending ReturnInterface -- rather than going through all signatures and updating them to FailObject|SuccessObject|WarnObject.
As noted by bishop, there is an RFC for adding multiple return types. However, I thought I'd add that as of PHP7.1 you can now specify a nullable return type like this:
function exampleFunction(string $input) : ?int
{
// Do something
}
So this function would take in a string and by adding the question mark before int you are allowing it to return either null or an integer.
Here's a link to the documentation:
http://php.net/manual/en/functions.returning-values.php
And here's a quote from that page explaining the usage:
PHP 7.1 allows for void and null return types by preceding the type declaration with a ? — (e.g. function canReturnNullorString(): ?string)
Also, here's another thread that relates to this: Nullable return types in PHP7
PHP from 7.2 onward supports the object return type
http://php.net/manual/en/migration72.new-features.php
function test(object $obj) : object
// return any type of object ...
Since PHP 8.0 this is possible.
You can now use union types to specify this:
function test(): Success|Failure
{
if ($this->condition === false) {
return new Failure();
}
return new Success();
}
The fact that this is possible does not mean that it is always advisable. In many (probably most) situations, using an interface (e.g. Result, which both Failure and Failure would implement) as advised in a different answer, is still much preferable.
But there are other instances where union types could make sense to an alternative to weak typing. E.g. a method that accepts both string and int, or to describe the return type of a function like stripos(), which returns int|false.
This ins't correct way:
function test(): ?
{
if ($this->condition === false) {
return FailObject;
}
return SucceedObject;
}
Multiple return type is a bad practice. Good practices:
You should define a exception:
class FailObjectException extends \Exception
{
private $exampleExtraInfo;
public function __construct($exampleExtraInfo, $message)
{
parent::__construct($message);
$this->exampleExtraInfo = $exampleExtraInfo;
}
public function exampleExtraInfo(): int
{
return $this->exampleExtraInfo;
}
}
Now, you can define function like:
function test(): SucceedObject
{
if ($this->condition === false) {
throw new FailObjectException(...,...);
}
return SucceedObject;
}
And use this function with try/catch:
try{
$succeedObject = $any->test();
} catch (FailObjectException $exception){
//do something
}

PHP interfaces. Different return type

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).

Anonymous function for a method of an object [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Calling closure assigned to object property directly
Why this is not possible in PHP? I want to be able to create a function on the fly for a particular object.
$a = 'a';
$tokenMapper->tokenJoinHistories = function($a) {
echo $a;
};
$tokenMapper->tokenJoinHistories($a);
PHP tries to match an instance method called "tokenJoinHistories" that is not defined in the original class
You have to do instead
$anon_func = $tokenMapper->tokenJoinHistories;
$anon_func($a);
Read the documentation here especially the comment part.
With $obj->foo() you call methods, but you want to call a property as a function/method. This just confuses the parser, because he didn't find a method with the name foo(), but he cannot expect any property to be something callable.
call_user_func($tokenMapper->tokenJoinHistories, $a);
Or you extend your mapper like
class Bar {
public function __call ($name, $args) {
if (isset($this->$name) && is_callable($this->$name)) {
return call_user_func_array($this->$name, $args);
} else {
throw new Exception("Undefined method '$name'");
}
}
}
(There are probably some issues within this quickly written example)

Categories