PHP type hinting in Laravel - php

I was wondering if there is any difference in using type hint in method parameters, such as:
function xyz (UserRepository $repo)
and doing the same stuff with annotations:
function xyz ($repo)
/* #var UserRepository $repo */

Using a type hint like
function xyz (UserRepository $repo)
is an instruction that the compiler will understand and react to. This means, PHP will raise an error when you try to pass anything that is not a UserRepository.
Quoting from the PHP Manual:
Type declarations allow functions to require that parameters are of a certain type at call time. If the given value is of the incorrect type, then an error is generated: in PHP 5, this will be a recoverable fatal error, while PHP 7 will throw a TypeError exception.
Using the "annotation" is just documentation. It's just a hint for your IDE and a developer. It has no functional meaning when executing PHP. A user would be able to pass whatever he wants to the function, e.g.
$somethingElse = new SomethingElse;
xyz($somethingElse);
This would work, but then it will fail when trying to access methods on $somethingElse that belong to the UserRespository. With a type hint, you would fail early.
Note: I put annotation in quotes because it's not really an annotation as you know from other languages, like Java. PHP doesn't have annotations as of 7.2. Userland implementations exist though.
Prefer real type hints.
This is not a Laravel specific thing by the way. Type Hints are a native PHP feature.

In addition to what Gordon said, Laravel uses type-hints for automatic injection: https://laravel.com/docs/5.5/container#automatic-injection

Related

PHP Intelephense 'undefined method' issues

I recently switched to an IDE that supports PHP's Intelephense.
There are two issues showing up that I don't know how to resolve (note, however, that the code executes correctly).
#1. Undefined method 'myfunc'
$myobj = $this->myfactory->createObject($myparams);
Essentially, I'm using a library that uses Factories to create the needed dependencies. I access the factory in question like stated above, where createObject's return type is an interface. The default class that implements this factory has the additional method myfunc, so I can validly call my_func(), but the IDE shows an error because it obviously can't find it in the interface.
Is there no easy way to cast the interface to the expected concrete class?
#2. Undefined method 'get'
class MyClass{
//...
static function withAdditionalParams($myParams){
return function ($defaultParamA, $defaultParamB) use ($myParams) {
return $this->get(self::class)->__invoke($defaultParamA, $defaultParamB, $myParams);
};
}
}
While being similar to the problem above, this one is a bit more complex. Since we're dealing with closures, the $this in this context actually refers to a DI Container, while Intelephense thinks it's referring to a MyClass instance.
Is there no way to explicitly say what $this refers to in this specific context?
Any help or advice is highly appreciated, thank you!!

PHP-DI Potentially polymorphic call when using the set method

The issue
I have an unexpected warning from PHPStorm when I try to set a new value in a PHP-DI container.
Given the following code:
function inject(Psr\Container\ContainerInterface $container){
$container->set(RandomClass::class, new RandomClass());
}
$container = new DI\Container(); class is instantiated
inject($container);
The following warning is triggered
Potentially polymorphic call. does not have members in its hierarchy
I understand what the warning means, but I do not see why it pops up, especially since I have not found any occurrences of this situation while looking on Google and SO and the documentation does not mention it.
Is there something I am missing, or is this a "false positive" ?
The set() method is not part of Psr\Container\ContainerInterface.
If you want to use that method, you can't typehint against the interface because your code explicitly needs a PHP-DI instance.
Your code doesn't have to be generic, don't overthink things too much. The PSR is useful mostly for frameworks and libraries (who need to be compatible with multiple containers), not for end-users.
The day you switch container library you will have many more complex things to do than just replacing the set() call.
The reason behind the issue
Given the following code (which is very similar to the one I use)
function inject(Psr\Container\ContainerInterface $container){
$container->set(RandomClass::class, new RandomClass());
}
$container = new DI\Container(); class is instantiated
inject($container);
The $container->set(...) call is going to trigger the following warning
Potentially polymorphic call. does not have members in its hierarchy
This is to be expected as Psr\Container\ContainerInterface only contains definitions for the following methods
get($id)
has($id)
The solution
Two possible solutions for this issue:
Type the methods directly with the container, making sure to not use the FQN of the class but only use Container and "use the namespace", it will make changing to a new container package easier (because this is still the goal behind PSRs, being able to almost hot-swap packages).
Create a custom interface based on Psr\Container\ContainerInterface and add the required methods to it.
Or, eventually, you can try to make PHP-FIG extend the PSR-11 standard to include a standard set($id, $value) method.

Why to use variable hinting in Laravel or PHP

PHP is not a strict typing language right? I am working with Laravel 5.1 and PHP 7.2
I would make my route like this
Route::get('/getSizes', 'JobTickeController#getSizes')->name('getSizes');
in JobTicketController there I define my function as below
public function getSizes(Request $response){
}
My question is even if PHP is not a strict typing (by default) why should I define Request variable type before $request variable? This code throws an exception with out the type hinting Request.
Although others go into the fact that PHP is not a strictly typed language, there's no answer to why the type hinting is necessary.
What happens in your Laravel app is the following. Laravel will attempt to hit the getSizes method in your controller by resolving any typehinted arguments from "the (Service) Container" also known as IoC. In case it finds no bindings, it will attempt to instantiate the class. Any arguments that have no type hinting are assumed to be "route parameters".
As your route has no parameters, the $request cannot be given any value without being typehinted. When you type hint the $request argument with the Illuminate\Http\Request class Laravel will automatically resolve that binding from its container and make it available inside your method.
TLDR:
Your exception is thrown in case you don't typehint because there's no route parameter to use as value.
Using typehinting will have Laravel automatically resolve a binding from the container or instantiate the class if it has not been bound.
See Method Inject in the Documentation.
First there is nothing named Loose Type, but you can say PHP is not a strictly typed language like JAVA for example.
In your example, that is a Type Hinting, so here you force the function to accept parameters with type Request only.

How to enforce a resource as a function argument (type declaration)

I'd like to make sure that a function only accepts a resource as a parameter.
For any other type, I would just write something similar to
function my_function(string $my_string) { }
and PHP will throw a TypeError whenever I pass anything other than a string to it. Nice.
The PHP documentation on the subject, does not list the resource as a type declaration candidate.
I know I can use something like
function my_function($resource) {
if (!is_resource($resource)) {
throw new InvalidArgumentException('resource expected, ' .
gettype($resource) . ' given');
}
// do stuff that expects the resource
}
However, this feels wrong. In my opinion, type declaration / hinting exists for good reasons and I don't understand why a resource could not be used (I can understand why a trait would not be allowed).
(On a side note, a solution for the old behavior where not even scalar types weren't allowed also implements checking for resources. So, I'm not the only one looking for it...)
There is one argument I could think of why a resource can not be declared: being a resource still can mean a lot of things since it could be any kind of resource. However, this argument would also hold for the array, a type that can be declared!
Is the code above the best way to proceed? Are there other ways? Or am I on the wrong track? Am I missing some point?
I am not looking for the PHPDoc-solution. While that would aid development, it won't catch wrong arguments at run-time.
In PHP a resource is not similar to for instance a file descriptor in C. There is no single interface. For instance both xml_parser_create and fopen return resources but have distinct interfaces. So forcing a function argument to be of type resource. Does not really say much.
Your best option is properly to create the interface you need for your class and create a default wrapper class implementing this interface for the resource functionality in question. The will give users of your class the most freedom.

How do I prevent using the incorrect type in PHP?

PHP, as we all know is very loosely typed. The language does not require you to specify any kind of type for function parameters or class variables. This can be a powerful feature.
Sometimes though, it can make debugging your script a painful experience. For example, passing one kind of object into a method that expects a different kind of object can produce error messages complaining that a certain variable/method doesn't exist for the passed object. These situations are mostly annoyances. More onerous problems are when you initialize one object with an object of the wrong class, and that "wrong object" won't be used until later on in the script's execution. In this case you end up getting an error much later than when you passed the original argument.
Instead of complaining that what I passed doesn't have a specific method or variable, or waiting until much later in script execution for my passed in object to be used, I would much rather have an error message, at exactly where I specify an object of the wrong type, complaining about the object's type being incorrect or incompatible.
How do you handle these situations in your code? How do you detect incompatible types? How can I introduce some type-checking into my scripts so that I can get more easily understood error messages?
Also, how can you do all this while accounting for inheritance in Php? Consider:
<?php
class InterfaceClass
{
#...
}
class UsesInterfaceClass
{
function SetObject(&$obj)
{
// What do I put here to make sure that $obj either
// is of type InterfaceObject or inherits from it
}
}
?>
Then a user of this code implements the interface with their own concrete class:
<?php
class ConcreteClass extends InterfaceClass
{
}
?>
I want ConcreteClass instances, and all future, unknown user-defined objects, to also be acceptable to SetObject. How would you make this allowable in checking for the correct type?
Actually for classes you can provide type hinting in PHP (5+).
<?php
class UsesBaseClass
{
function SetObject(InterfaceObject $obj)
{
}
}
?>
This will also work correctly with inheritance as you would expect it to.
As an aside, don't put the word 'object' in your class names...
as an addition to Eran Galperin's response you can also use the type hinting to force parameters to be arrays - not just objects of a certain class.
<?php
class MyCoolClass {
public function passMeAnArray(array $array = array()) {
// do something with the array
}
}
?>
As you can see you can type hint that the ::passMeAnArray() method expects an array as well as provide a default value in case the method is called w/o any parameters.
For primitive types you could also use the is_* functions :
public function Add($a, $b)
{
if (!is_int($a) || !is_int($b))
throw new InvalidArgumentException();
return $a + $b;
}
You see, there are multiple answers about type hinting. This is the technical solution. But you should also make sure that the whole design is sensible and intuitive. This will make type problems and mistakes more rare.
Remember that even these type failures will be thrown at runtime. Make sure you have tests for the code.
Even in the case you describe, your script will crash, complaining there is no method / attribute X on the object Y so you'll know where does this come from.
Anyway, I think that always try to prevent grew up programmers to pass the wrong object to a method is not a good time investment : you could spend it in documenting and training instead.
Duck typing and careful colleagues is what you need, not additional checks that will make you app more rigid.
But it may be a Pythonista point of view...
#Eran Galperin's response is the preferred method for ensuring the object you are using is of the correct type.
Also worth noting is the instanceOf operator - it is helpful for when you want to check that an object is one of multiple types.
You can set the error_reporting ini setting in your php.ini file or use error_reporting function to set it in run time

Categories