Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulate a generic class in PHP

I'm trying to implement a Results class that processes queries. So, simply put, you would have functions like this:

function all();
function first();
function paginate(int $perPage, int $pageNo = 1);

This works pretty well, the problem being that the IDE has no possible way of knowing the return type when this same results class is being used in multiple different query classes. Example:

UserQuery->results()->all() will return an array of User entities.

UserQuery->results()->first() will return a single User entity.

In some languages, you have generics, which means I could just use Results<User> in the UserQuery class and then my Results class could return T[] and T respectively.

One idea I had was to pass an empty entity as the constructor to the Results class and then try to use that property as the return type, but I couldn't figure this out. Is there any workaround to this? The main problem I'm trying to solve is IDE autocompletion and analysis, so a pure phpDoc solution is perfectly fine for my use case.

The only other workaround I can come up with is having to write a separate Results class for each entity type, which would prove to be exhausting.

like image 472
Devon Avatar asked Apr 03 '19 14:04

Devon


People also ask

Will PHP get generics?

Now, to be clear: PHP doesn't have generics. That's a bold statement cutting quite a lot of corners, and we're coming back to that later in this series. But for now it's sufficient to say that what I'm showing next isn't possible in PHP. But it is in many other languages.

What is generic class in PHP?

Generics enable developers to create a whole family of declarations using a single generic declaration - for example, a generic collection-type declaration Collection<T> induces a declaration for any entity-type T , which negates the need to implement a dedicated collection-type for every entity-type, reducing the need ...

Can you instantiate a generic type?

A generic type is like a template. You cannot create instances of it unless you specify real types for its generic type parameters.

How do you declare a generic class?

The declaration of a generic class is almost the same as that of a non-generic class except the class name is followed by a type parameter section. The type parameter section of a generic class can have one or more type parameters separated by commas.


1 Answers

I don't think there's a way to do exactly what you described, but in similar cases I would suggest using a proxy class for each Results type and document proper return types using phpDocumentor's @method. This solution has added value of having a great place for any type specific Results modifications and expansions. Here's an example:

abstract class Results
{
    function all(): array
    {
    }

    function first()
    {
    }

    function paginate(int $perPage, int $pageNo = 1): array
    {
    }
}

class User { }

/**
 * @method User[] all()
 * @method User first()
 * @method User[] paginate(int $perPage, int $pageNo = 1)
 */
class UserResults extends Results { }

class UserQuery
{
    /**
     * @var UserResults
     */
    private $results;

    public function __construct()
    {
        $this->results = new UserResults();
    }

    public function results(): UserResults
    {
        return $this->results;
    }
}

$userQuery = new UserQuery();
$test = $userQuery->results()->all();

PhpStorm correctly recognises returned type

like image 116
Kwarcu Avatar answered Oct 08 '22 01:10

Kwarcu